A news feed is one decision — fanout-on-write vs fanout-on-read — and everything else follows from the arithmetic. Interviewers probe whether you can compute the amplification and find the hybrid.
Requirements and capacity
200M DAU; average user follows 300 accounts; 500M posts/day (~5.8k writes/s, 3x peak); each DAU loads their feed ~10x/day → 2B reads/day ≈ 23k QPS. p99 feed load <300ms. Post storage: 500M/day × ~1KB ≈ 500GB/day → ~180TB/year — cheap object/key-value storage, not the interesting part. The interesting part is the timeline cache.
The one decision
Fanout-on-write: each post is inserted into every follower's timeline at publish time. Cost per post = follower count. Average 300 → 1.7B timeline writes/day ≈ 20k writes/s sustained. Reads become one list fetch — fast, but writes are amplified by the follower graph.
Fanout-on-read: store the post once; assemble the timeline by merging the recent posts of each followee at read time. Cost per read = follower count × merge work. Writes stay cheap; reads pay 300 merges each at 23k QPS — that's ~7M merges/s. Unaffordable.
The hybrid (what real systems do): fanout-on-write for users under ~10k followers; celebrities' posts are fetched and merged into the precomputed list at read time. The threshold is where followers × write_cost crosses merge_cost × read_rate — say it as an equation, not a magic number. 99.9% of users get the push path; the 0.1% who would generate more than half the amplification get the pull path.
Data model
posts(post_id, author_id, content, ts, flags); follows(follower_id, followee_id, created_at); timeline:{user_id} → Redis list of post IDs, capped at ~1000. Cache stores IDs, not payloads: IDs are ~8 bytes each and rankable later; payloads come from a post cache on render. Storing payloads in the timeline means every edit re-ranks the list — storing IDs means the feed can change ranking without a rewrite.
Pipeline
Post service → queue → fanout workers → one list insert per follower (batched). The queue decouples publish latency from fanout cost: the author sees their post live while workers drain. Worker lag under a burst is visible as slow timeline refresh for followers, never as a failed publish — say that tradeoff explicitly.
Tradeoffs
Chronological vs ranked: chronological lists are cacheable and cheap; ranked feeds need score recomputation on interaction (like/comment events) — push rank updates through the same queue and re-sort in place. Cold users: compute their timeline on first read, evict by recency — 200M users × 800B of IDs ≈ 160GB only if everything is cached; the cache holds the hot users.
Bottlenecks
Fanout workers during posting peaks; the celebrity read-merge path; timeline cache memory; and write amplification to the DB on the push path. The feed is a memory and queue problem — name that and the architecture answers itself.