"Design Twitter's feed" comes down to one question the whole interview circles around: when someone you follow posts, when does it reach your feed — at write time or at read time? Push it to every follower the moment it's posted and reads are instant, until a celebrity with 100M followers posts and you're doing 100M writes. Compute it fresh on every load and writes are trivial, until reads merge hundreds of accounts under 200ms. The real answer is "both, depending on who posted" — and knowing when to switch is the whole game.
This is the condensed walkthrough; the full guide (estimates, API, data model, ranking, caching, and the full production .NET 9 code) is on my site 👇
Full guide: https://prepstack.co.in/blog/design-a-news-feed-system-design
The design at a glance
| Concern | Decision |
|---|---|
| Fan-out | Hybrid — push for normal users, pull for celebrities |
| Feed store | Per-user precomputed feed of post IDs in Redis (capped) |
| Reads | Read precomputed feed + merge recent celebrity posts |
| Ranking | Reverse-chronological baseline; ranked = separate ML service |
| Consistency | Eventually consistent — a post can take seconds to appear |
| Content | Store post IDs in feeds; hydrate from a post cache at read |
Why fan-out is the whole interview
Say 500M daily users, ~10 feed loads/day, ~2 posts/day:
Feed reads: 500M x 10 / 86,400s ~ 58,000 reads/sec (peak ~150k+)
Posts: 500M x 2 / 86,400s ~ 11,600 posts/sec
Reads dwarf writes — so optimize for reads (precompute). But the real monster is fan-out amplification: a normal post reaches a few hundred feeds; a celebrity post with 100M followers, done naively, is 100M feed writes for one post. That one fact forces the hybrid.
POST path
Alice posts --> [ Post service ] --> store post
|
v
[ Fan-out service ] --> push postId into followers' feeds (Redis)
READ path (hot path)
Bob opens app --> [ Feed service ] --> read Bob's precomputed feed (post IDs, Redis)
--> hydrate content from [ Post cache ]
--> merge in recent CELEBRITY posts (pull)
--> return page
Push vs pull vs the hybrid
Fan-out on write (push). Push the post ID into every follower's precomputed feed. Feed loads are O(1) cache reads — perfect for read-heavy. But a post costs one write per follower, and you pay it even for followers who never open the app.
Fan-out on read (pull). Store each post under its author only; at read time, fetch recent posts from everyone you follow and merge. Posting is one cheap write — but reads fetch from hundreds of followees, merge and sort, on the hot path, every load.
The celebrity problem. Pure push breaks on high-follower accounts (one post = tens of millions of writes). Pure pull breaks on normal reads (merging hundreds of followees every load). Neither extreme survives.
The hybrid (what to ship):
Your feed = [ precomputed feed from normal followees (PUSH) ]
(+) merged with
[ recent posts pulled from celebrity followees (PULL) ] at read
Normal users push to followers' precomputed feeds; celebrities skip fan-out and get merged in at read time. You do a little pull work at read, but only for the handful of huge accounts you follow — bounded and cheap — while the mass of normal posts stays precomputed.
Ranking, caching & scaling
- Ranking: reverse-chronological is the simple, defensible baseline. A ranked feed adds a scoring service (a whole ML subsystem) — don't hand-wave it, don't disappear into it.
- Store IDs, not content. Feeds hold post IDs; the post is stored once and hydrated from a post cache at read. Otherwise you copy a viral post into millions of feeds.
- Cap feed length. Precompute only the latest ~hundreds of IDs; deep history on demand.
- Skip inactive users. Don't maintain feeds for accounts that haven't logged in — rebuild on next login.
-
Shard posts and feeds by
userId; the fan-out service is embarrassingly parallel. - Read-your-own-writes: show a user their own new post immediately even though global fan-out is eventual.
I shipped this in production (Mattrx)
Mattrx members follow campaigns and open an activity feed of recent conversion.tracked, budget.threshold.crossed, and anomaly.detected events. V1 computed it at read time: a fan-in scan across every followed campaign against a 1.2B-row CampaignEvents table — up to ~120k rows for a power user — paid in tail latency. Moving to a hybrid fan-out-on-write model (normal campaigns push each event id into per-follower Redis lists capped at 500; campaigns with ≥5,000 followers skip fan-out and merge at read):
| Metric | Before (computed-at-read) | After (hybrid precomputed) |
|---|---|---|
| Activity-feed load p95 | ~600 ms | ~35 ms |
| Activity-feed load p99 | ~1,400 ms | ~90 ms |
| Azure SQL round-trips per open | 1 fan-in query | 0 (served from Redis) |
| Rows scanned per open (peak follower) | up to ~120k | 0 |
| Campaigns on the pull path | n/a | ~0.01% (≥5,000 followers) |
| Redis working set for feeds | n/a | ~2.3 GB (500-id cap) |
The fan-out worker (a MediatR notification handler) runs off the request path — the domain change commits, then a background worker publishes the event — so fan-out cost never touches the API's 120ms p95 budget. A {tenantId} hash tag co-locates a tenant's feeds on one Redis Cluster slot, and an LTRIM after each push keeps a hot follower's feed bounded at 500 ids. (Full .NET 9 handler is in the post.)
The model to carry forward
A news feed is a read-heavy system you make fast by precomputing feeds on write — except where that's too expensive. Push posts into followers' cached feeds so reads are O(1); fall back to pulling at read for the rare celebrity whose fan-out would be a write storm; merge the two. Store IDs not content, cap the feed, skip the inactive, and be honest that ranking is its own world.
Three habits it teaches: lead with the fan-out decision (push vs pull vs hybrid is the interview); reach for the celebrity yourself before you're asked; precompute the common case, compute the rare one.
The full guide has the estimates, API, data model, the celebrity threshold, ranking/caching/sharding, the complete production .NET 9 fan-out worker, and the "when it's overkill" section:
https://prepstack.co.in/blog/design-a-news-feed-system-design
Originally published on PrepStack.
Top comments (0)