Design a News Feed
Prompt
We're building a social product. Users follow each other and post short updates, and when I open the app I should see a feed of recent posts from the people I follow — the freshest and most relevant first. Some of our users are going to be big accounts with a lot of followers. Design the feed.
How this round runs
"See a feed of posts from people I follow" sounds like one query, but the brief quietly drops a landmine — "some users have a lot of followers." You drive: surface the scale and freshness requirements, then design it, and I'll push you hard on the fan-out trade-off and the celebrity/hot-key problem, and make you commit.
Model answer
1. Requirements I'd surface first.
- Read:write ratio? Feeds are read-dominated — people open the app far more than they post. Say 100:1 reads to writes. That biases me toward precomputing reads.
- Freshness: must a new post appear in followers' feeds in seconds, or is a minute fine? Seconds pushes me toward fan-out-on-write; "eventually" gives more options.
- Follower distribution — the landmine. "Some users have a lot of followers" is the whole problem. If the max is a few thousand, one strategy works; if a celebrity has 50M followers, naive fan-out is catastrophic. I'd ask for the distribution explicitly.
- Ranking: pure reverse-chronological, or ranked by relevance? Ranking changes where I can cache and how I paginate.
- Pagination: infinite scroll → I need a stable cursor, not offset paging (offset breaks when new posts shift everything).
- Scale: say 500M users, 200M daily active, avg 200 follows, power-law follower counts.
Non-functional: feed open is the hot path — it must be fast and cacheable; the system must not fall over when a celebrity posts.
2. High-level design.
post --> write service --> post store (source of truth)
|
fan-out decision (per author's follower count)
|
fan-out-on-write: push post id into fan-out-on-read: leave it in the
each follower's precomputed feed cache author's outbox; pull at read time
\ /
feed-read service: merge precomputed feed + pulled celebrity posts --> rank --> paginate (cursor)
Two ways to build a feed, and the right answer is both:
- Fan-out-on-write (push): when you post, write your post id into every follower's precomputed feed list (in a cache). Reads are then trivial — just read your list. Great read latency.
- Fan-out-on-read (pull): leave posts in each author's "outbox"; at read time, gather the people you follow and merge their recent posts. Cheap writes, expensive reads.
3. Deep-dive: the fan-out trade-off and the celebrity/hot-key problem. This is the entire question.
Fan-out-on-write gives wonderful reads but its write cost is proportional to follower count. For a normal user (a few hundred followers) that's nothing. For a celebrity with 50M followers, a single post triggers 50M writes — a write storm that hammers the cache, lands as a giant hot key, and delays the post for everyone. That's the hot-key problem: the work is wildly skewed by the power-law follower distribution.
Fan-out-on-read has the opposite shape: the celebrity's post is written once (cheap), but now every reader does extra work merging celebrity outboxes at read time — and reads are the 100× hot path, so you've moved cost to the worst place to put it.
The committed answer: a hybrid, split by follower count. I'd fan-out-on-write for normal accounts (most posts, cheap fan-out, fast reads) and fan-out-on-read for the handful of high-follower accounts. A reader's feed is then: their precomputed list (from normal follows, pushed) merged at read time with the recent posts of the few celebrities they follow (pulled). This keeps the write storm off celebrity posts and keeps reads fast for the common case, because almost everyone you follow is a normal account.
4. A committed trade-off and its cost. I commit to the hybrid, threshold on follower count. The cost I name out loud: complexity and a fuzzy boundary. I now maintain two code paths and a threshold that's a tuning knob, not a law of nature — set it wrong and either celebrities slip into the write-fan-out (storm returns) or too many mid-size accounts fall into read-fan-out (reads slow down). And the read path is no longer a single cache read — it's "read precomputed feed + pull N celebrity outboxes + merge + rank," which is more moving parts to get right and monitor. I accept that complexity because the alternative — one uniform strategy — is either a write storm or a slow read for everyone, and there's no uniform strategy that's good at both ends of a power-law.
5. Operational concerns. Failure mode: a celebrity posts and, despite the hybrid, the merge/pull path for that post gets hammered — the celebrity's outbox becomes a read hot key as millions pull it simultaneously. I'd defend by caching the celebrity's recent posts aggressively (they're read by millions, change rarely — ideal cache target) and coalescing concurrent pulls. Detection: hot-key metrics on the cache and read-latency-by-feed-type. Another failure: the fan-out-on-write workers fall behind during a spike, so normal users' feeds go stale — I'd detect via fan-out queue lag and degrade gracefully (a slightly stale feed beats a stalled one). Rollback: fan-out strategy and threshold are config, so I can shift the boundary or force a struggling account to pull-mode without a deploy.
- Surfaced the follower distribution as the central requirement, not an afterthought
- Explained both fan-out-on-write and on-read with their true cost shapes
- Identified the celebrity/hot-key problem as a power-law write storm and named why uniform strategies fail
- Committed to the hybrid and named its cost (two code paths + a tuning threshold + a more complex read)
- Used cursor pagination and aggressive caching of celebrity posts, and reasoned about fan-out lag, unprompted
- 'A celebrity with 50M followers posts — what happens with fan-out-on-write?' → 50M writes, a write storm and hot key; pull-on-read for high-follower accounts instead
- 'Then why not pull-on-read for everyone?' → reads are the 100x hot path; pushing read cost onto every feed-open is the worst place to put it
- 'Where's the threshold, and what if you set it wrong?' → it's a tuning knob; too high re-creates the storm, too low slows reads — monitor and adjust as config
- 'How do you paginate an infinite feed without dupes/gaps?' → cursor on (timestamp, post id), not offset, so new posts don't shift the window
- 'What breaks at 10x writes?' → fan-out workers lag and feeds go stale; degrade to slightly-stale rather than stalling