DumbPhobia#013 The RADIO Framework

Frontend system design interviews are notoriously open-ended. RADIO is a battle-tested framework that gives you a repeatable structure to tackle any frontend design problem with clarity and confidence.


What is RADIO?

RADIO is an acronym for the five phases of answering a frontend system design question:

LetterPhaseWhat you do
RRequirements clarificationDefine the scope and constraints
AArchitectureSketch the high-level system structure
DData modelDefine the data your app will work with
IInterface definitionDesign the API between components and server
OOptimizationsIdentify performance and UX improvements

Instead of jumping straight into implementation details, RADIO forces you to think like a senior engineer — understanding the problem fully before proposing solutions.


Why RADIO?

Frontend system design questions are intentionally vague. An interviewer might say:

"Design a news feed like Twitter/X." "Build an autocomplete search bar." "Design a photo sharing app like Instagram."

Without a framework, most candidates either:

  • Start coding immediately without clarifying scope
  • Talk about random performance tips with no structure
  • Forget entire dimensions of the problem (accessibility, error states, offline support)

RADIO gives you a mental checklist to cover every dimension systematically, shows the interviewer you think in systems, and keeps the conversation organized.


R — Requirements Clarification

The first and most critical phase. Never skip this. Asking the right questions upfront separates senior engineers from junior ones.

What to clarify

Functional requirements — what the product actually does:

  • What are the core features? Which are in scope vs. out of scope?
  • Who are the users? What devices do they use?
  • What's the expected scale? (users, data volume, request frequency)

Non-functional requirements — the quality attributes:

  • Performance targets (LCP, TTI, INP thresholds)
  • Availability requirements (offline support?)
  • Accessibility requirements (WCAG compliance level?)
  • Internationalization (multiple languages, RTL support?)
  • Authentication and authorization model?

Example — "Design a news feed"

Questions to ask:
✓ Is this a social feed (personalized) or a topic-based feed?
✓ Do users create posts or just consume them?
✓ Should the feed update in real time or on refresh?
✓ Do we need to support images, video, or just text?
✓ What's the target device — mobile-first or desktop?
✓ Is infinite scroll or paginated navigation expected?
✓ Do we need offline support?

Output of this phase

A scoped problem statement you can reference throughout the interview:

"We're building a personalized social news feed that supports text and image posts, infinite scrolling, real-time updates via WebSocket, and needs to be mobile-first with a target LCP under 2.5 seconds."

Pros of doing this well:

  • Prevents wasted time designing the wrong thing
  • Demonstrates product thinking alongside technical thinking
  • Gives you constraints that justify your later decisions

Common mistakes:

  • Accepting the vague prompt at face value and jumping to solutions
  • Asking too many questions — focus on the ones that will genuinely change your design
  • Forgetting non-functional requirements entirely

A — Architecture

Now sketch the high-level structure of the system. This is where you identify the major components and how they interact.

What to cover

  • Rendering strategy — SSG, SSR, CSR, ISR, or Partial Pre-Rendering?
  • Component breakdown — what are the major UI sections?
  • Data flow — where does data come from and how does it move through the app?
  • Client-server boundary — what lives on the client vs. server?
  • Infrastructure — CDN, caching layers, WebSocket servers, etc.

Rendering strategy decision

Choosing the right rendering strategy is the first architectural decision and should be justified:

News feed posts      → SSR (personalized, can't be pre-built)
User profile pages   → SSG + ISR (mostly static, occasionally updated)
Chat sidebar         → CSR (real-time, highly interactive)
Landing/marketing    → SSG (purely static, max performance)

Component architecture diagram

For a news feed, the high-level architecture might look like:

┌─────────────────────────────────────┐
│              Client                  │
│                                      │
│  ┌──────────┐    ┌────────────────┐  │
│  │  NavBar  │    │   Feed Shell   │  │
│  └──────────┘    │                │  │
│                  │  ┌──────────┐  │  │
│  ┌──────────┐    │  │ PostCard │  │  │
│  │ Sidebar  │    │  ├──────────┤  │  │
│  │  (CSR)   │    │  │ PostCard │  │  │
│  └──────────┘    │  ├──────────┤  │  │
│                  │  │ PostCard │  │  │
│                  │  └──────────┘  │  │
│                  └────────────────┘  │
└──────────────────────────────────────┘
          │                │
     REST API          WebSocket
          │                │
┌─────────────────────────────────────┐
│              Server                 │
│  ┌──────────┐    ┌────────────────┐ │
│  │ API GW   │    │  Feed Service  │ │
│  └──────────┘    └────────────────┘ │
│  ┌──────────┐    ┌────────────────┐ │
│  │   CDN    │    │   DB / Cache   │ │
│  └──────────┘    └────────────────┘ │
└─────────────────────────────────────┘

Output of this phase

A diagram or verbal description of:

  • The major UI components and how they're composed
  • The chosen rendering strategy with justification
  • The client-server communication pattern (REST, GraphQL, WebSocket)
  • Any infrastructure pieces (CDN, cache layer)

Pros of doing this well:

  • Shows system-level thinking, not just component-level thinking
  • Gives you a map to refer back to throughout the interview
  • Lets you justify later decisions (e.g., "because we chose SSR here, we need rehydration...")

Common mistakes:

  • Going too deep into one component before sketching the whole system
  • Choosing a rendering strategy without explaining why
  • Forgetting about infrastructure (CDN, caching) entirely

D — Data Model

Define the data your application will work with — both the shape of entities and how state is managed on the client.

What to cover

  • Entities — what are the core data objects?
  • Relationships — how do they relate to each other?
  • Client state — what lives in memory vs. on the server?
  • State management strategy — local state, global store, server state (React Query)?

Entity design — news feed example

// Core entities
interface User {
  id: string;
  username: string;
  displayName: string;
  avatarUrl: string;
  followersCount: number;
}

interface Post {
  id: string;
  authorId: string;
  content: string;
  imageUrls: string[];
  likesCount: number;
  commentsCount: number;
  createdAt: string;
  isLikedByCurrentUser: boolean;
}

interface FeedPage {
  posts: Post[];
  nextCursor: string | null;
  hasMore: boolean;
}

Client state breakdown

Not everything belongs in the same state layer:

Server state (React Query)
  └─ Feed posts          — fetched, cached, paginated
  └─ User profiles       — fetched on demand
  └─ Notifications       — polled or pushed via WebSocket

Global UI state (Zustand / Context)
  └─ Current user session
  └─ Theme preference
  └─ Unread notification count

Local component state (useState)
  └─ Is compose modal open?
  └─ Input field value
  └─ Dropdown open/closed

Essential state principle

Apply the essential state principle — only store the minimum data needed to derive the full UI. Derived values should be computed, not stored:

// ❌ Don't store derived values
const [posts, setPosts] = useState([]);
const [postCount, setPostCount] = useState(0); // derived from posts.length
const [hasMore, setHasMore] = useState(true);  // derived from nextCursor

// ✅ Store only essentials, derive the rest
const [posts, setPosts] = useState([]);
const [nextCursor, setNextCursor] = useState<string | null>(null);
const postCount = posts.length;       // derived
const hasMore = nextCursor !== null;  // derived

Pros of doing this well:

  • Shows you think about data architecture, not just UI
  • Demonstrates knowledge of server state vs. client state distinction
  • Catches over-engineering early

Common mistakes:

  • Storing everything in one global state blob
  • Not distinguishing between server state and UI state
  • Forgetting about relationships between entities (e.g., posts need to know if the current user liked them)

I — Interface Definition

Define the contracts between your frontend and backend — the API endpoints, request/response shapes, and real-time communication protocols.

What to cover

  • REST endpoints or GraphQL queries/mutations
  • Request parameters — filters, pagination, sorting
  • Response shapes — what data is returned
  • Real-time interfaces — WebSocket events if applicable
  • Error handling contracts — what errors can the API return?

REST API design — news feed example

GET /api/feed
  Query params:
    cursor?: string       // for cursor-based pagination
    limit?: number        // default 20
  Response:
    {
      posts: Post[],
      nextCursor: string | null,
      hasMore: boolean
    }

GET /api/posts/:postId
  Response: Post

POST /api/posts
  Body: { content: string, imageUrls?: string[] }
  Response: Post

POST /api/posts/:postId/like
  Response: { likesCount: number, isLiked: boolean }

DELETE /api/posts/:postId/like
  Response: { likesCount: number, isLiked: boolean }

WebSocket events — real-time feed updates

// Events the server sends to the client
type ServerEvent =
  | { type: 'NEW_POST';        payload: Post }
  | { type: 'POST_LIKED';      payload: { postId: string; likesCount: number } }
  | { type: 'POST_DELETED';    payload: { postId: string } }
  | { type: 'NOTIFICATION';    payload: Notification };

// Events the client sends to the server
type ClientEvent =
  | { type: 'SUBSCRIBE_FEED';  payload: { userId: string } }
  | { type: 'PING' };

GraphQL alternative

If using GraphQL instead of REST:

query GetFeed($cursor: String, $limit: Int = 20) {
  feed(cursor: $cursor, limit: $limit) {
    posts {
      id
      content
      likesCount
      isLikedByCurrentUser
      author {
        id
        username
        avatarUrl
      }
    }
    nextCursor
    hasMore
  }
}

mutation LikePost($postId: ID!) {
  likePost(postId: $postId) {
    likesCount
    isLiked
  }
}

Pros of doing this well:

  • Shows you can think across the full stack
  • Lets you identify over-fetching or under-fetching early
  • Demonstrates knowledge of pagination strategies (cursor vs. offset)

Common mistakes:

  • Designing APIs that over-fetch (returning entire user objects when only name and avatar are needed)
  • Forgetting pagination entirely
  • Not defining error response shapes
  • Ignoring real-time requirements discovered in the Requirements phase

O — Optimizations

The final phase — layer in performance improvements, resilience patterns, and UX enhancements. By now you've earned the right to talk about these because they're grounded in the actual design.

Performance optimizations

Rendering performance:

Virtualize the feed list          → react-virtual / react-window
                                     (only mount visible posts, unmount the rest)

Memoize PostCard component        → React.memo
                                     (prevent re-renders when parent updates)

Lazy load images                  → loading="lazy" or Intersection Observer
                                     (defer off-screen images)

Lazy load heavy components        → React.lazy + Suspense
                                     (compose modal, full-screen image viewer)

Network performance:

Cache feed data with staleTime    → React Query (5 min stale time for feed)
Debounce search input             → 300ms debounce before API call
Prefetch next page                → useInfiniteQuery prefetchNextPage
CDN for static assets             → images, fonts, JS bundles
Brotli compression                → content negotiation in HTTP headers

Bundle performance:

Bundle splitting by route         → Webpack / Vite code splitting
Tree shaking                      → remove unused library code
Critical CSS inlining             → above-the-fold styles in <head>

UX & resilience optimizations

Optimistic updates:

// Like a post — show the result immediately, roll back on failure
const [optimisticLiked, toggleOptimistic] = useOptimistic(
  post.isLikedByCurrentUser,
  (current) => !current
);

Error handling:

Network errors        → Retry with exponential backoff (React Query retries: 3)
Render errors         → Error boundaries per feed section
Empty states          → Thoughtful empty state UI, not blank screens
Stale data            → Background refetch on window focus (React Query default)

Offline support:

Service Worker        → Cache static assets and last-seen feed
IndexedDB             → Persist draft posts while offline
Optimistic queue      → Queue mutations, replay when back online

Accessibility:

ARIA live regions     → Announce new posts to screen readers
Focus management      → Trap focus in modals
Keyboard navigation   → Full keyboard support for actions
Reduced motion        → Respect prefers-reduced-motion for animations

Pros of doing this well:

  • Shows you know not just how to build, but how to build well
  • Demonstrates awareness of real-world constraints (slow networks, accessibility, errors)
  • Ties back to requirements — optimizations justify themselves by the targets you set in phase R

Common mistakes:

  • Leading with optimizations before establishing the baseline design
  • Optimizing for things not in scope (e.g., talking about SSG optimizations for a real-time app)
  • Forgetting UX optimizations (error states, empty states, loading states)

Putting It All Together

Here's how a full RADIO interview response flows for "Design a news feed":

R — Requirements (~5 min)
    "Let me clarify the scope before jumping in..."
    → Personalized feed, text + images, infinite scroll,
      real-time updates, mobile-first, LCP < 2.5s

A — Architecture (~8 min)
    "At a high level, the system looks like this..."
    → SSR for initial load, CSR for real-time updates,
      component breakdown, CDN + WebSocket layer

D — Data model (~5 min)
    "The core entities are Post and User, structured like..."
    → Entity shapes, client state layers,
      essential state principle applied

I — Interface (~7 min)
    "The API contracts between client and server..."
    → REST endpoints with cursor pagination,
      WebSocket event schema

O — Optimizations (~5 min)
    "Given our LCP target, here are the key improvements..."
    → List virtualization, lazy loading, React Query caching,
      optimistic updates, error boundaries, a11y

Total: ~30 minutes — a well-paced system design interview.


RADIO vs. Improvising

Without RADIOWith RADIO
CoverageRandom, incompleteSystematic, thorough
Justification"I'd use React Query""Given our real-time requirement from phase R, React Query with WebSocket integration fits here"
ConfidenceTrails off under pressureCheckpoints keep you on track
Interviewer signalUnclear seniority levelClear senior-level systems thinking
OptimizationsMentioned randomlyGrounded in requirements and design decisions

Quick Reference Card

R  Requirements
   ├─ Functional:     core features, user types, scale
   └─ Non-functional: performance, a11y, i18n, offline, auth

A  Architecture
   ├─ Rendering strategy (SSG / ISR / SSR / CSR / PPR)
   ├─ Component breakdown
   ├─ Data flow diagram
   └─ Infrastructure (CDN, WebSocket, cache)

D  Data model
   ├─ Core entities and relationships
   ├─ Client state layers (local / global / server)
   └─ Essential state (minimize, derive the rest)

I  Interface
   ├─ REST endpoints or GraphQL schema
   ├─ Pagination strategy (cursor vs. offset)
   ├─ WebSocket event contracts
   └─ Error response shapes

O  Optimizations
   ├─ Rendering:  virtualization, memoization, lazy loading
   ├─ Network:    caching, debounce, prefetch, CDN, compression
   ├─ Bundle:     splitting, tree shaking, critical CSS
   ├─ UX:         optimistic updates, empty/error/loading states
   └─ A11y:       ARIA, focus management, keyboard nav

RADIO isn't a rigid script — it's a thinking tool. The phases naturally overlap and loop back. A good interviewer will push you deeper on any phase; RADIO ensures you've laid the groundwork to answer confidently from every angle.