Design a News Feed: System Design Interview 2026

·17 min read
By ·Updated
system-designfan-outnews-feedarchitecturebackendinterview-preparation

A news feed joins durable publication, eligibility, candidate generation, ranking, and serving. Fan-out is one important cost, but so are inactive recipients, hot authors, changing follows and blocks, deletion and moderation, model freshness, and tail latency. The interview design below uses a hybrid push/pull candidate strategy and treats its traffic numbers as assumptions to validate, not facts about every social product.

This walkthrough assumes the 6-step system design framework and applies it at senior depth. It is Part 5 of a system design series.

Table of Contents

  1. The Problem
  2. Step 1 - Clarify Requirements
  3. Step 2 - Estimate Scale
  4. Step 3 - API and Data Model
  5. Step 4 - High-Level Design
  6. Step 5 - Deep Dive: Fan-Out on Write, on Read, and the Hybrid
  7. Step 6 - Bottlenecks and Trade-offs
  8. Reference Architecture
  9. Common Mistakes in the Interview
  10. Quick Reference
  11. Frequently Asked Questions
  12. Sources
  13. Related Articles

The Problem

We are designing the home feed of a social platform: when a user opens the app, they see recent posts from the accounts they follow, in a useful order. The canonical examples are the Twitter/X home timeline and the Instagram feed.

Assume a read-heavy aggregation over a producer set with extreme skew. Materialising candidates can reduce read work but amplifies writes; retrieval on demand avoids writes but adds scatter-gather and tail latency. A defensible design bounds both paths and can move producers between them based on measured cost rather than a brand-name “celebrity” rule.


Step 1 - Clarify Requirements

Functional requirements:

  • A user can publish a post.
  • A user can view their home feed: recent posts from accounts they follow.
  • The feed is paginated for infinite scroll.

Out of scope (name, then defer): the follow/social-graph service itself - we assume getFollowers(userId) and getFollowees(userId) exist - the media storage for post content, and the machine-learning ranking model, which we treat at the system level only.

Non-functional requirements:

  • Scenario read-heavy. Start with a 50:1 feed-open-to-post ratio, then test sensitivity.
  • Latency and freshness SLOs. Define candidate, ranking, hydration, and end-to-end budgets instead of assuming 200 ms everywhere.
  • Bounded staleness objectives. Publication is asynchronous, but deletes, blocks, legal restrictions, and moderation may need much faster enforcement than ordinary freshness.
  • Extreme fan-out skew. Most accounts have hundreds of followers; a few have tens of millions. This is the defining constraint.

Settle whether the product is chronological or ranked, followed-only or recommended, and whether ads or other inventories are blended. This walkthrough uses a ranked followed-accounts feed, while keeping eligibility and current-visibility checks explicit.


Step 2 - Estimate Scale

The arithmetic here is what exposes the celebrity problem.

Reads. Assume 500 million daily active users opening the feed ~10 times/day: 5 billion feed reads/day ≈ ~58,000 reads/sec average, perhaps ~250,000/sec at peak.

Posts. At ~0.2 posts/user/day, that is 100 million posts/day ≈ ~1,200 posts/sec average.

Fan-out amplification. With an average of ~200 followers per account, fan-out on write turns 100M posts into 100M x 200 = 20 billion feed insertions/day ≈ ~230,000 inserts/sec. That is the routine cost of pushing.

The high-fan-out number. An account with 100 million followers could request 100 million candidate insertions from one post under pure push. At the scenario's average 230,000 insertions/sec, that equals about 7.2 minutes, not four days - but arriving as one burst still creates a serious per-key, queue, network, and regional-skew problem.

Storage. 800 × 16 bytes × 500M is about 6.4 TB only for a hypothetical 16-byte entry payload. Real entries need IDs, stable retrieval metadata, visibility/version data and storage-engine overhead; then add indexes, allocator overhead, replication, backups and headroom. Measure the encoded entry and active-user retention policy rather than sizing the cluster from the lower bound.


Step 3 - API and Data Model

POST /api/posts
  body: { "authorId": "...", "content": "..." }
  201 Created   { "postId": "...", "version": 1 }
 
GET /api/feed?cursor=<opaque>
  200 OK   { "items": [ ... ], "nextCursor": "<opaque>" }

The core entities:

EntityKey fields
PostpostId, authorId, content, createdAt - the source of truth
Social graphfollower-followee edges; accessed via the follow service
Feed candidatesuserId -> bounded references such as (postId, authorId, eventTime, source, policyVersion) - derived

One implementation is a per-user ordered candidate projection, capped by age and count according to measured recall. Do not persist a mutable personalised ranking score as though it were stable: candidate retrieval and final scoring have different lifecycles. Partition hot users and batch hydration; a generic sorted set is an option, not the data model.

Pagination is cursor-based. For chronological order, a cursor can carry an ingestion-time/ID boundary. For mutable personalised ranking, use a short-lived feed-session or candidate-snapshot ID with position and model/version context, or explicitly accept re-ranking gaps and deduplicate client-side. Sign and expire cursors; re-check current visibility on every page. A raw (score, postId) cursor is not stable when scores, deletes, follows, or policy change.


Step 4 - High-Level Design

The write path and the read path are separated by a fan-out stage and a queue.

flowchart TD
    Client([Client]) -->|POST post| PS[Post Service]
    PS -->|post + outbox event| PStore[(Posts Store)]
    PStore --> OP[Outbox Publisher]
    OP -->|at-least-once new-post event| Q[Fan-Out Queue]
    Q --> FW[Fan-Out Workers]
    FW -->|getFollowers| Graph[(Social Graph)]
    FW -->|insert postId| Feeds[(Feed Store<br/>per-user sorted sets)]
    Client -->|GET feed| FS[Feed Service]
    FS -->|read precomputed| Feeds
    FS -->|pull celebrity posts| PStore
    FS -->|hydrate IDs| PCache[(Post Cache)]
    FS -->|rank + merge| Client

Figure 1. The post and outbox event commit together before a publisher delivers the event at least once. Fan-out workers update an idempotent candidate projection; the read path retrieves, filters, hydrates, ranks and blends candidates under a deadline.

Accept a post only after its durable state and outbox intent commit. The API may return 201 when the post exists or 202 if creation itself is asynchronous, but it must document the boundary. The outbox publisher and queue may redeliver, so fan-out updates use (postId, recipientId, projectionVersion) idempotency and ordering rules. The feed service retrieves bounded candidates, filters current eligibility, batch-hydrates features and content, ranks and blends under a deadline.


Step 5 - Deep Dive: Fan-Out on Write, on Read, and the Hybrid

This is the core. The question is when the feed is assembled, and the answer is "it depends on the poster" - which is the hybrid model.

Fan-out on write (push)

When a user posts, immediately insert that post's ID into the precomputed feed of every follower.

Candidate retrieval becomes cheaper, not trivial: the read path must still enforce blocks, deletion, privacy, moderation, source quotas and deduplication, hydrate features/content, rank, blend, and paginate. The cost moves toward publication: a post with F eligible active followers can create up to F projection updates, delivered asynchronously and idempotently.

It becomes inefficient for high predicted fan-out, frequent publishers, inactive recipients, constrained regions, or degraded queue capacity. A static follower count is only one signal; active audience and expected reads determine whether materialisation is useful.

Fan-out on read (pull)

When a user opens their feed, query the recent posts of every account they follow, then merge and rank.

Now publication avoids per-recipient projection writes, but a naive read can scatter to every followed author and inherit the slowest shard's latency. Production retrieval batches author keys by shard, queries replicated author-timeline indexes in parallel with deadlines, limits candidates per source, and tolerates partial results. Whether this is cheaper depends on the scenario's read/write ratio and active graph, not a universal 50:1 rule.

flowchart LR
    subgraph Push["Fan-out on write (push)"]
        P1[User posts] -->|F insertions now| P2[Every follower feed]
        P3[Reader opens feed] -->|1 read| P4[Done - precomputed]
    end
    subgraph Pull["Fan-out on read (pull)"]
        L1[User posts] -->|1 write| L2[Posts store]
        L3[Reader opens feed] -->|N queries| L4[Merge + rank now]
    end

Figure 2. Push materialises recipient candidates before reads; pull retrieves author candidates during reads. Either can be appropriate. The hybrid policy should minimise measured total cost and tail latency while meeting freshness and correctness constraints.

The hybrid model

For this scenario, combine the strategies using a dynamic materialisation policy:

  • Cost-effective producers push references into eligible active recipients' candidate projections.
  • Expensive producers or overloaded paths keep posts in author timelines for on-read retrieval.
  • At feed-read time, the service reads the materialised projection and batch-fetches on-read inventories by shard, then filters, deduplicates, ranks and blends them.

This can bound both costs only if the system caps work: limit materialisation batches, retrieval sources, candidates per source, wall-clock deadlines, retries and per-tenant budgets. A viewer may follow many high-cost accounts, so “only a handful” is an assumption to measure, not a guarantee.

sequenceDiagram
    participant U as User
    participant FS as Feed Service
    participant F as Feed Store
    participant P as Posts Store
 
    U->>FS: GET /feed
    FS->>F: read precomputed feed (push portion)
    F-->>FS: post IDs from normal accounts
    FS->>P: pull recent posts from followed celebrities
    P-->>FS: celebrity post IDs
    Note over FS: merge + rank both sets
    FS-->>U: ranked, hydrated feed page

Figure 3. The hybrid read merges a materialised candidate projection with batched on-read inventories. Shard-level batching, deadlines, candidate caps and partial-result policy bound the work; the diagram's single posts-store call represents that retrieval layer, not one request per author.

The policy can begin with follower count but should incorporate active recipients, publication frequency, regional distribution, queue lag, fan-out completion cost, feed-open probability and product priority. Reclassify with hysteresis so an author near the boundary does not oscillate between paths, and version the policy so duplicate events converge.

Ranking and feed assembly

A chronological feed needs a defined event/ingestion-time tie-breaker. A ranked feed typically separates candidate generation from later filtering, feature hydration, scoring, blending, diversity and policy stages. Rank a bounded set under a deadline and define fallbacks for missing features or model failure. Evaluate relevance and quality offline, validate with experiments, and monitor guardrails such as latency, complaints, creator concentration, unsafe-content exposure and distribution shifts. The author's session may overlay a newly committed post for read-your-writes, but current privacy and moderation still win.

Consistency model

Candidate propagation is eventually consistent, with a freshness SLO derived from the product. Visibility revocation is a separate, often stricter path: every page rechecks deletes, blocks, privacy and moderation against authoritative or safely cached policy state. The candidate store is derived, but a correct rebuild may need retained events and checkpoints for posts, follows, unfollows, edits, deletes and policy changes; pulling current posts from current followees is not always equivalent.

Failure modes

  • Fan-out backlog. Queue age raises freshness lag. Apply admission control, fair per-author/tenant partitions, dynamic push-to-pull reclassification, bounded retries and worker scaling; a queue stores pressure but does not remove it.
  • Duplicate or reordered events. At-least-once delivery requires idempotent projection keys and version checks. Deletes, edits and visibility changes must not be overwritten by a late create event.
  • Feed-store shard loss. Serve a bounded pull fallback only if downstream capacity permits, otherwise return a degraded partial feed while rebuilding from retained events and checkpoints.
  • Hot post or author timeline. Layer caching, request coalescing, replication and origin admission control. A cache alone can stampede on miss or invalidation.
  • Ranking dependency failure. Use deadlines, cached/default features, a simpler deterministic fallback, and model/version telemetry rather than failing the entire feed.

Multi-region

One option is viewer-home-region candidate storage plus globally distributed post and graph services. That still requires explicit event routing, residency rules, replication lag, failover ownership, duplicate suppression and backfill after a region outage. A local post replica can lag behind a candidate reference; hydration needs a retry/fallback policy. Blocks, privacy and moderation changes need a propagation target and fail-closed/fail-open decision. State RPO/RTO and whether a user moving regions reads the old home region, migrates state, or temporarily receives a partial feed.

Evolution path

StageApproach
LaunchSimple author timelines and bounded on-read retrieval when the graph and traffic are small
GrowthAdd fan-out on write so the hot read path is precomputed
ScaleHybrid by poster type, ranked feed, multi-region feed stores

Define an opaque cursor contract and stable chronological tie-breaker early; a ranked snapshot/session can be added when needed. Keep durable post state separate from candidate projections. Add materialisation, ML ranking, recommendations and multi-region only when product requirements and measurements justify their cost.

Observability

Track end-to-end feed latency plus candidate, graph, feature, ranking and hydration spans; durable-post-to-visible freshness; oldest outbox/queue age; dropped, duplicate and reordered events; projection/rebuild lag; pull fan-out width and partial-result rate; cache origin load; policy-filter counts; model version, score distributions and fallback rate; cursor duplicate/gap reports; and per-tenant fairness. Set SLOs by product and region rather than copying 200 ms or 30 seconds as universal targets.


Step 6 - Bottlenecks and Trade-offs

  • High fan-out is one bottleneck; dynamic materialisation, active-recipient targeting, quotas and flexible retrieval contain it.
  • Feed read latency needs bounded retrieval, deadlines, batching, caching, fallbacks and partial-result semantics, even with precomputation.
  • Candidate-store memory is controlled by age/count caps chosen from measured recall, plus compression, tiering and active-user policies.
  • Ranking cost is contained by ranking only a bounded candidate set, never the whole follow graph.
  • Pagination stability requires a declared ordering boundary; mutable ranking usually needs a short-lived session/snapshot plus deduplication and visibility rechecks.

Reference Architecture

The pattern this problem teaches, reusable well beyond feeds:

Materialise candidates when expected recipient-read benefit exceeds fan-out cost, retrieve other inventories on read through bounded shard-level batching, and merge both under one eligibility, ranking, pagination, and observability contract.

flowchart LR
    subgraph Common["Common case - precomputed"]
        C1[Post] -->|push, async| C2[(Per-user feeds)]
    end
    subgraph Tail["Skewed tail - computed on read"]
        T1[Celebrity post] --> T2[(Posts store)]
    end
    Read[Feed read] --> C2
    Read --> T2
    Read --> Merge[Merge + rank]

Figure 4. Two candidate paths end at one merge. “Common” and “tail” are outcomes of a measured, versioned policy rather than permanent account classes. Both paths still need eligibility checks, idempotency, deadlines and capacity limits.

The transferable idea is adaptive materialisation: compare the cost and freshness of precomputing a projection with retrieving it on demand, then retain the ability to change policy as traffic and product semantics evolve.


Common Mistakes in the Interview

  • Choosing pure push or pure pull and never confronting the celebrity problem.
  • Offset-based pagination on a feed whose head shifts continuously, producing duplicates and gaps.
  • Pushing into inactive users' feeds, spending write work on feeds no one will read.
  • Fanning out synchronously in the post request path instead of off a queue.
  • Publishing an event after the post commit without an outbox/log, allowing a durable post to lose its only fan-out intent.
  • Treating the candidate store as source of truth, or assuming it can be rebuilt from only current posts and follows.
  • Forgetting read-your-writes for the author's own post.
  • Storing full post bodies in every feed instead of IDs hydrated at read time.
  • Skipping delete/block/privacy/moderation checks, idempotency, event ordering, inactive users or failure fallbacks.
  • Encoding a mutable score in a cursor without snapshot/session semantics.

Quick Reference

TopicKey Point
Core patternAdaptive materialisation plus bounded on-read retrieval and one merge/rank path
Fan-out on writeLower candidate-read work; amplified, asynchronous, idempotent projection writes
Fan-out on readLower publish amplification; batched shard scatter, deadlines and partial results
High-cost producerClassify by predicted active fan-out cost and demand, not one follower threshold
RankingCandidate generation, eligibility, features, scoring, blending, diversity and policy
PaginationSigned, expiring cursor tied to stable order or ranked session/snapshot
Candidate storeBounded derived projection with retained events/checkpoints for rebuild
ConsistencyFreshness SLO plus stricter current visibility and revocation checks
DeliveryPost + outbox commit; at-least-once events; projection keys and versions deduplicate
Multi-regionDeclare home region, routing, lag, residency, failover, RPO/RTO and policy propagation

Frequently Asked Questions

What is the difference between fan-out on write and fan-out on read?

Fan-out on write materialises a post reference into recipient candidate feeds asynchronously, moving work toward publication. Fan-out on read keeps author timelines and retrieves candidates when a viewer requests a feed, moving work toward serving. Push can lower read latency but amplifies writes and wastes work for inactive users; pull avoids that amplification but creates scatter-gather and tail-latency pressure. The right mix depends on measured read/write rates, active audience, fan-out cost, and freshness SLOs.

How do you handle celebrity accounts in a news feed?

Use a hybrid policy: push when predicted fan-out cost and active-recipient benefit are acceptable, and retrieve on read for expensive producers or inventories. Do not classify only by a permanent follower-count threshold; include posting rate, active audience, tenant and region distribution, queue pressure, and current read demand. At read time, batch requests to author-timeline shards, cap candidates, apply deadlines, and merge partial results rather than issuing one unbounded call per celebrity.

How is a news feed ranked?

A feed normally has at least two stages: candidate generation from eligible inventories, then filtering, feature hydration, scoring, blending, diversity and policy checks over a bounded set. The materialised feed should store candidate references and stable retrieval metadata, not pretend a mutable personalised score is final. Ranking goals need offline evaluation, online experiments, guardrail metrics, bias and abuse review, and a chronological mode if the product promises one.

Why use cursor-based pagination for a feed instead of offset?

Offset pagination is unstable when new candidates enter or items are removed. A cursor should be opaque, signed, expiring, and tied to a stable ordering boundary: for a chronological feed, an ingestion-time and ID tuple; for a ranked feed, usually a feed-session or candidate-snapshot ID plus position and model/version context. A raw mutable score and ID do not prevent gaps or duplicates after rescoring, deletion, privacy changes, or inventory refresh.

Is the precomputed feed the source of truth?

No. It is a derived candidate projection. Rebuilding requires retained post events, current and historical eligibility inputs, social-graph changes, deletes, blocks, moderation and model policy; a simple pull from current followees may not recreate the prior feed. Keep source events and reconciliation checkpoints, and use a bounded pull fallback only when serving capacity allows. Every read must re-check current visibility because derived entries can be stale.

How fast must a new post appear in followers' feeds?

Freshness is a product SLO, not a universal number. Measure durable-post-to-eligible-feed visibility and define separate targets for ordinary, degraded, and overloaded periods. The author's session can overlay a newly committed post for read-your-writes, but must still respect deletion, moderation and privacy. Followers receive it through the asynchronous candidate path, with queue age, dropped events and reconciliation monitored explicitly.


Sources


This is Part 5 of the core track in a 16-part system design series. Next: Design a Chat System.

Ready to ace your interview?

Get 550+ interview questions with detailed answers in our comprehensive PDF guides.

View PDF Guides