"Fan-out on write vs fan-out on read"
The core trade-off behind feeds, timelines and inboxes: precomputing per-user timelines on write versus assembling them on read, the celebrity problem, hybrid fan-out, ranking, storage cost and how to explain it in an interview.
Reading is half of it. See this used in a real interview: walk through Design a News Feed →
Every feed question (Twitter, Instagram, Facebook, LinkedIn) comes down to one decision: when a user posts, do you copy the post into every follower's timeline now, or do you gather posts from everyone a user follows when they open the app? The first is fan-out on write (push); the second is fan-out on read (pull). Getting this trade-off right, and knowing why big products use a hybrid, is most of a strong feed answer.
Fan-out on write (push)
When Alice posts, a worker looks up her followers and appends the post id to each follower's precomputed timeline, usually a capped list in Redis or a wide-column table keyed by user id.
- Reads are trivial: open the app, read the first page of your timeline list, hydrate the post ids from a cache. One key, one round trip.
- Writes are expensive: one post becomes N timeline writes, where N is the follower count.
- Wasted work: you write into timelines of users who will never open the app this week.
- Latency of delivery: a post reaches followers over seconds or minutes as the fan-out queue drains.
Fan-out on read (pull)
When Bob opens the app, the feed service fetches recent posts from each account he follows and merges them.
- Writes are trivial: store the post once, in the author's own list.
- Reads are expensive: following 500 accounts means 500 lookups (or a scatter-gather across shards) and a merge on every refresh.
- Always fresh and no wasted work for inactive users.
- Tail latency suffers, because the slowest of hundreds of lookups sets the response time. See tail latency.
Side by side
| Fan-out on write | Fan-out on read | |
|---|---|---|
| Cost paid at | post time | read time |
| Read latency | very low | higher, variable |
| Write amplification | followers per post | none |
| Storage | a timeline per user | posts only |
| Best when | reads far outnumber writes, follower counts moderate | few readers, huge follower counts, or rare reads |
| Breaks on | celebrities with millions of followers | users who follow thousands of accounts |
Feeds are read-heavy (often 100 reads per write), which is why push is the default.
The celebrity problem
A celebrity with 100 million followers would turn one post into 100 million timeline writes, saturating the fan-out workers and delaying everyone else's posts. The standard answer is a hybrid:
- Normal accounts fan out on write.
- Accounts above a threshold (say 100 k followers) do not fan out; their posts stay in their own list.
- At read time, the feed service merges the user's precomputed timeline with recent posts from the few celebrities they follow.
Because a user follows only a handful of celebrities, the read-time merge stays small. The celebrity's recent posts are extremely hot, so cache them aggressively. See hot keys and skew.
Other optimisations
- Skip inactive users: only fan out to users active in the last N days; rebuild an inactive user's timeline on demand when they return.
- Store ids, not posts: timelines hold post ids (and maybe author id and timestamp); content is hydrated from a post cache, so edits and deletes apply everywhere.
- Cap timeline length: keep the latest 500 to 800 ids; older pages fall back to pull.
- Prioritise the queue: fan-out for users who are online right now first.
- Deletes and unfollows: filter at read time rather than rewriting millions of timelines; clean up lazily.
Ranking on top
Modern feeds are ranked, not chronological. The fan-out (or pull) step now builds a candidate set; a ranking service scores candidates with features (affinity with the author, engagement, recency) and returns the top items. The precomputed timeline is still useful: it is a cheap, fresh source of candidates. See recommendation systems.
Beyond social feeds
The same trade-off appears elsewhere:
- Chat: small groups fan out each message to members' inboxes; very large channels are read from the channel log instead. See Design Slack.
- Notifications: per-user inbox (push) versus computing "what's new" on open.
- Reddit-style front pages: one shared ranked list per community, computed periodically and read by everyone, which is fan-out on read with heavy caching. See Design Reddit.
How to present it
- State the read/write ratio and follower distribution from your estimates.
- Pick push as the default because reads dominate.
- Raise the celebrity problem yourself and introduce the hybrid threshold.
- Mention storing ids, capping timelines, and skipping inactive users.
- Add ranking as a layer on top of candidate generation.
Walk through it end to end in Design a News Feed and Design Instagram.
Checklist
- Read/write ratio and follower distribution estimated first.
- Push for normal accounts, pull for celebrities, merged at read.
- Timelines hold capped lists of ids, hydrated from a post cache.
- Inactive users skipped and rebuilt on demand.
- Deletes and unfollows filtered at read time.
- Ranking applied to the merged candidate set.