A complete frontend system design walkthrough — the kind of answer that gets you hired at Meta, Google, and top-tier companies.
If you've ever been asked "Design Facebook" in an interview, you know the sheer panic that follows. Facebook is massive — news feed, profiles, messenger, groups, stories, marketplace, notifications. Where do you even begin?
In this guide, I'll walk you through exactly how I'd approach this problem using the RADIO framework (Requirements, Architecture, Data Model, Interface Definition, Optimizations). This is the same framework used by engineers at Meta, Google, and Amazon in real interviews.
We're going to go deep. Not surface-level bullet points — actual code, actual trade-offs, actual production decisions. Let's go.
📋 Step 1: Requirements Exploration
The biggest mistake candidates make? Jumping straight into architecture. Don't. Spend the first few minutes narrowing scope.
Here's how I'd think about it:
Clarifying Questions I'd Ask
Question | Why It Matters | Assumed Answer |
|---|---|---|
Which part of Facebook are we designing? | Facebook has 50+ features. We need focus. | The News Feed — the core experience |
What types of posts should we support? | Text-only is simple. Media changes everything. | Text, images, videos, links with previews |
What interactions do users have with posts? | Determines component complexity | Like (with reactions), Comment, Share |
What pagination UX should we use? | Affects data fetching strategy entirely | Infinite scroll (not numbered pages) |
Do we need real-time updates? | WebSocket vs polling decision | Yes — new posts should appear without refresh |
Should we support post creation? | Determines if we need a composer component | Yes — text + media upload |
What devices should we target? | Responsive design implications | Desktop-first, responsive to mobile |
Do we need offline support? | Service worker + caching strategy | Nice-to-have, not core |
What are Functional vs. Non-Functional Requirements?
Before we dive into the specific list for Facebook, let’s clarify the two pillars of any system design requirements phase. Think of these as the Product vs. the Engineering perspective.
1. Functional Requirements (The "What")
These define the features. If you were a product manager, what would you put in the user's hands? Functional requirements describe the specific behaviors of the system—the actions a user can take and the responses they expect.
Example: "A user can click a 'Like' button to react to a post."
Focus: User workflows, UI components, and business logic.
2. Non-Functional Requirements (The "How")
These define the quality attributes and constraints. They don't describe what the system does, but how well it does it. This is where most senior-level architectural decisions are made.
Example: "The 'Like' button must respond within 100ms and work even if the user is on a slow 3G connection."
Focus: Performance (latency), Scalability, Accessibility, Reliability, and Security.
Rahul Rana’s Perspective: "When I was at Uber, we didn't just ask 'Can the user book a ride?' We asked 'How does this UI behave when the user is on a 2G connection in Mumbai with 1% battery?' That is the level of thinking I expect when you define your non-functional requirements."
Functional Requirements for Facebook News Feed
Based on our scope, we will focus on these core features:
Feed Consumption: Users see a scrollable list of posts (text, images, videos) from their network.
Infinite Scroll: New content loads automatically as the user reaches the bottom of the viewport.
Post Composer: A multi-media input area to create new posts with privacy settings.
Interactions: The ability to react (Like), comment in a nested thread, and share posts.
Real-time Updates: A notification or "New Posts" toast appears when new content is available in the feed.
Stories: A horizontal shelf at the top of the feed for ephemeral media.
Functional Requirements (What the system does)
Browse News Feed — Users see a scrollable list of posts from friends, pages, and groups
Infinite Scroll — More posts load automatically as user scrolls down
Post Interactions — Like (with reaction picker), comment, share
Create Posts — Text, images, videos, with audience selector
Real-time Updates — New posts appear at the top without page refresh
Stories Bar — Horizontal scrollable stories at the top
Non-Functional Requirements (How well it does it)
Performance — Feed loads in under 2 seconds. Scrolling maintains 60fps.
Scalability — Handle millions of concurrent users
Accessibility — Screen reader compatible, keyboard navigable
SEO — Not critical for authenticated feeds, but good for public pages
Offline Resilience — Graceful degradation when network drops
💡 Pro Tip from Rahul: In real interviews, requirements exploration should take about 10-15% of your time. The interviewer wants to see that you can scope a problem — not that you can list every feature Facebook has ever built.
🏗️ Step 2: Architecture / High-Level Design
Now that we know what we're building, let's design the architecture. Remember — this is a frontend system design. We're not designing the backend fan-out service or the ranking algorithm. We're designing the client-side architecture.
Component Architecture
Component Responsibilities
Component | Responsibility | Key Decisions |
|---|---|---|
App Shell | Layout orchestration, routing, auth context | Renders once, never re-renders on feed updates |
Header/Nav | Navigation, search, notifications badge | Fixed position, lazy-load notification dropdown |
Stories Bar | Horizontal scrollable story thumbnails | Virtualized horizontal list, preload adjacent stories |
Post Composer | Create new posts with text, media, audience | Expands on focus, media upload with preview |
Feed List | Infinite scroll container for feed posts | Virtualized list, intersection observer for loading |
Feed Post | Individual post rendering | Polymorphic — renders differently for text/image/video/link |
Post Actions | Like, comment, share buttons | Optimistic updates, reaction picker on long-press/hover |
Comments Section | Threaded comments under a post | Lazy-loaded, collapsed by default, paginated |
Sidebar | Contacts list, trending topics, sponsored content | Sticky position, independent data fetching |
Rendering Strategy: SSR + CSR Hybrid
This is a critical architectural decision. Let's explore three approaches:
Approach 1: Pure Client-Side Rendering (CSR)
Pros | Cons |
|---|---|
Simple deployment (static hosting) | Slow initial load (blank screen → content) |
Rich interactivity | Poor SEO (not critical for auth'd feed) |
Easy to reason about state | Large JS bundle blocks rendering |
Approach 2: Server-Side Rendering (SSR)
Pros | Cons |
|---|---|
Fast First Contentful Paint (FCP) | Server cost per request |
Content visible before JS loads | Hydration complexity & mismatches |
Better perceived performance | TTFB depends on server/data latency |
Approach 3: Streaming SSR + Selective Hydration (Facebook's Actual Approach)
Pros | Cons |
|---|---|
Best of both worlds — fast FCP + rich interactivity | Complex infrastructure (streaming server) |
Progressive hydration — critical UI interactive first | Requires React 18+ and careful Suspense boundaries |
Non-blocking — sidebar can load independently | Debugging hydration issues is painful |
💡 Recommendation: For a Facebook-scale app, Approach 3 is the winner. Facebook actually pioneered this approach. The initial HTML streams in with the critical above-the-fold content, and React progressively hydrates components as their JS chunks arrive. This gives users a perceived load time of <1 second even on slow connections.
📦 Step 3: Data Model / Core Entities
The data model defines what flows through your system. Getting this right is crucial — a bad data model means constant refactoring later.
Core Entities
Why This Data Model Works
1. Polymorphic PostContent: Instead of cramming everything into one flat object, we use discriminated unions. This means the rendering layer can switch on content.type and TypeScript will narrow the type automatically:
2. Denormalized User in Post: We embed the User object directly in the post instead of just a userId. Why? Because every single post needs to render the author's name and avatar. A normalized approach (storing only userId and looking up separately) would require an additional lookup for every post — that's death by a thousand queries.
3. UTC Timestamps: Always send raw UTC timestamps from the server, never pre-formatted strings. The client can format them using Intl.RelativeTimeFormat:
Client-Side State Structure
Why Map + Ordered Array? This is a common pattern in production apps. The Map<string, FeedPost> gives us O(1) lookups when we need to update a specific post (e.g., after a reaction). The feedOrder: string[] maintains display order. This separation is crucial because:
Updating a reaction only modifies the Map entry — the order array is untouched, so the feed list doesn't re-render
Adding new posts to the top only modifies the order array
Deduplication is trivial — just check if the ID exists in the Map
🔌 Step 4: Interface Definition (API Design)
This is where most candidates go shallow. Don't just say "we'll have a REST API." Define the actual contracts.
API Overview
Source | Destination | Protocol | Purpose |
|---|---|---|---|
Server | Client | REST (HTTP) | Fetch feed, create posts, CRUD operations |
Server | Client | WebSocket / SSE | Real-time new posts, live reactions |
Client | CDN | HTTP | Static assets, media files |
REST API Contracts
1. Fetch Feed (Cursor-based Pagination)
Why cursor-based over offset-based pagination?
Feature | Offset-based ( | Cursor-based ( |
|---|---|---|
New posts inserted at top | ❌ Causes duplicates on next page | ✅ Cursor is stable |
Deleted posts | ❌ Causes items to shift, skipping content | ✅ Cursor remains valid |
Performance at scale | ❌ | ✅ Index seek, constant time |
Simplicity | ✅ Easy to implement "page 3 of 10" | ❌ No random page access |
Best for | Static content, admin panels | Dynamic feeds, chat, infinite scroll |
For a news feed with constant insertions, cursor-based is the only sane choice.
2. Create Post
Media Upload Strategy: Two approaches:
Approach A: Direct Upload
Pros: Simple. Cons: Blocks post creation until upload completes. Large files timeout.
Approach B: Pre-signed URL Upload (Recommended)
Pros: Non-blocking, handles large files, upload directly to CDN. Cons: More complex, needs cleanup for abandoned uploads.
3. React to Post
WebSocket Contract (Real-Time Updates)
This is what separates a good answer from a great one. Let's design the real-time layer.
Connection Lifecycle
WebSocket Message Types
Real-Time Update Strategy: The "New Posts" Banner
Here's a subtle but important UX decision. When new posts arrive via WebSocket, we have two options:
Option A: Auto-insert at top (Twitter-style)
Problem: If you're reading a post mid-screen, the content jumps down. This is infuriating.
Option B: Buffer + Banner (Facebook-style) ✅ Recommended
Why this is better: The user stays in control. They choose when to see new content. No jarring layout shifts.
SSE vs WebSocket: When to Choose What
Feature | WebSocket | Server-Sent Events (SSE) |
|---|---|---|
Direction | Bidirectional | Server → Client only |
Protocol | WS/WSS (custom) | HTTP (standard) |
Reconnection | Manual implementation | Built-in auto-reconnect |
Binary data | ✅ Supported | ❌ Text only |
HTTP/2 multiplexing | ❌ Separate connection | ✅ Shares connection |
Browser support | Excellent | Excellent (no IE) |
Best for | Chat, gaming, collaborative editing | News feeds, notifications, live scores |
For a news feed specifically, SSE is actually a great choice because updates are server-to-client only. However, if you need to support typing indicators or read receipts in comments, WebSocket is necessary.
⚡ Step 5: Optimizations & Deep Dive
This is where you differentiate yourself. Let's go section by section.
5.1 Feed List Performance — Virtualization
A typical user might scroll through 200+ posts in a session. Without virtualization, that's 200+ complex DOM nodes in memory. The browser chokes.
Approach comparison for long lists:
Approach | DOM Nodes | Memory | Scroll Perf | Implementation |
|---|---|---|---|---|
No virtualization | All rendered | High | Degrades over time | Simple |
Windowed virtualization | ~15-20 | Low | Consistent 60fps | Moderate |
Hybrid (keep first N, virtualize rest) | ~50 | Medium | Good | Complex |
5.2 Infinite Scroll with Intersection Observer
Why rootMargin: '400px'? This triggers the load 400px before the user reaches the bottom. It creates the illusion of infinite content — by the time they scroll to where the sentinel was, new content is already rendered.
5.3 Optimistic Updates for Reactions
Nobody wants to wait 200ms to see their like register. Optimistic updates make the UI feel instant:
5.4 Image Loading Strategy
Why this matters at scale:
Aspect ratio box prevents Cumulative Layout Shift (CLS) — the image space is reserved before the image loads
Blurred thumbnail placeholder (LQIP) gives immediate visual feedback
loading="lazy"defers off-screen imagessrcSetserves appropriately sized images — no loading a 4K image for a 680px container
5.5 Accessibility Deep Dive
Key accessibility patterns:
role="feed"— tells screen readers this is a dynamic feedaria-posinset/aria-setsize— position context within the feed (-1 for unknown total)aria-busy— announces loading statearia-pressedon reaction buttons — toggle button patternKeyboard navigation: Tab between posts, Enter to interact
5.6 Performance Budget & Monitoring
Metric | Target | How to Measure |
|---|---|---|
First Contentful Paint (FCP) | < 1.5s | Web Vitals API |
Largest Contentful Paint (LCP) | < 2.5s | Web Vitals API |
Cumulative Layout Shift (CLS) | < 0.1 | Web Vitals API |
Interaction to Next Paint (INP) | < 200ms | Web Vitals API |
JS Bundle Size (initial) | < 200KB gzipped | Webpack Bundle Analyzer |
Feed scroll FPS | Consistent 60fps | Chrome DevTools Performance panel |
Time to Interactive (TTI) | < 3s on 3G | Lighthouse |
5.7 How Things Break at Scale
Let's talk about real failure modes — the stuff that crashes at 1M+ concurrent users:
Problem 1: WebSocket Connection Storms
When a server goes down, all connected clients try to reconnect simultaneously. This is called a thundering herd.
Solution: Exponential backoff with jitter (shown in the WebSocket code above). Each client waits a random time before reconnecting, spreading the load.
Problem 2: Memory Leaks from Infinite Scroll
User scrolls through 500 posts → 500 post components in memory → browser tab crashes.
Solution: Virtualization (shown above) + aggressive cleanup of detached media elements:
Problem 3: Stale Data After Tab Switch
User opens Facebook, switches to another tab for 2 hours, comes back. The feed is stale, the WebSocket is dead.
Problem 4: Race Conditions in Optimistic Updates
User likes a post, then quickly unlikes it. The first API call hasn't returned yet. The second call fires. Now we have conflicting state.
Problem 5: Bundle Size Explosion
As features grow (reactions picker, media player, comment composer, link previews), the main bundle balloons.
🔑 Summary: What a Great Answer Looks Like
Section | Key Points to Hit | Time |
|---|---|---|
Requirements | Scope to News Feed. Identify: infinite scroll, post types, reactions, real-time updates. | ~3 min |
Architecture | Component tree with clear responsibilities. Rendering strategy (SSR + streaming). State management approach. | ~7 min |
Data Model | Polymorphic PostContent. Denormalized User. Normalized store (Map + order array). | ~5 min |
API Design | Cursor-based pagination. WebSocket contract with reconnection. Optimistic updates. | ~7 min |
Optimizations | Virtualization, IntersectionObserver, LQIP images, code splitting, accessibility. | ~8 min |
Final thoughts from Rahul: The difference between a "pass" and a "strong hire" isn't knowing every optimization. It's showing that you think in trade-offs. Every decision has a cost. Cursor pagination is better for dynamic data but loses random page access. Virtualization saves memory but adds complexity. WebSocket gives real-time but needs reconnection logic. The best candidates don't just pick the "right" answer — they explain why it's right for this specific use case.
Next up: Design Instagram — where we'll tackle image-heavy feeds, stories, explore grids, and the unique challenges of media-first applications.