Why architecture matters here

The reason this is fundamentally an architecture problem is the extreme read/write asymmetry combined with per-user personalization. A shared, non-personalized resource can be cached once and served to everyone; a feed cannot, because every user's is different. So you must either compute N personalized lists (one per user, push) or compute a list on demand N times (pull), and the choice of which — and where to place the dividing line — determines the entire cost structure of the system. There is no way to sidestep it with a bigger cache alone.

Fan-out on write wins the common case precisely because reads dominate. If a post is read far more often than it is written, then moving work from the read path to the write path is a net reduction in total work, and it converts the expensive part (assembling a personalized list) from a synchronous, latency-critical operation into an asynchronous, background one. The user waiting for their feed gets a precomputed answer; the fan-out happens off the critical path after the author has already received their 'posted' confirmation. Latency where it matters is the payoff.

But write amplification is the cost, and it is superlinear in the wrong place. Fan-out on write turns one write (the post) into F writes (one per follower). For a median user with a few hundred followers that is fine. For a celebrity it is catastrophic, and worse, it is bursty — a celebrity posting during a peak event can inject tens of millions of timeline writes in seconds, saturating the fan-out workers and delaying fan-out for every ordinary user whose posts are stuck behind the celebrity's in the queue. The tail of the follower distribution poisons the whole pipeline.

Fan-out on read has the opposite profile and the opposite problem. A post is a single write no matter how many followers, so there is no amplification — ideal for celebrities. But every feed read becomes a wide scatter-gather across all your followees' recent posts, which is expensive and slow, and it is paid on the latency-critical path billions of times. Pure pull does not scale for ordinary users for the same reason pure push does not scale for celebrities: it puts the heavy work in the wrong place for the dominant traffic pattern.

This is why the hybrid is not a compromise but the correct answer: it applies each strategy where its cost profile is favorable. Push, where amplification is bounded (ordinary accounts), keeps the dominant read path cheap. Pull, where amplification would be ruinous (celebrities), keeps writes cheap for exactly the accounts that would otherwise break push. The read path merges the two. Understanding the shape of the follower distribution — heavily skewed, a long tail of huge accounts — is what tells you where to place the push/pull threshold, and that placement is the single most consequential decision in the design.

Advertisement

The architecture: every piece explained

The post store is the source of truth for content. When a user posts, the text, media references, author id, and timestamp are written here durably and assigned a post id. Everything else in the feed system deals in post ids, not post bodies — timelines are lists of ids, and the actual content is fetched (hydrated) from the post store only at the last moment when a feed is rendered. Keeping timelines as id lists is what makes them cheap to store and update.

The follower graph answers the two questions the system constantly asks: who follows this author (for fan-out on write) and whom does this user follow (for fan-out on read). It is a large, skewed graph that must be queried at high rate, so it is typically sharded and heavily cached. The fan-out service reads the follower list to know where to push; the read path reads the followee list to know whom to pull from for celebrity merging.

The fan-out service is the write-time engine. On a post event it looks up the author's follower count and routes: for an ordinary author it fetches the follower list and, asynchronously, inserts the post id into each follower's timeline cache; for a celebrity author (above a follower threshold) it skips push entirely, leaving that post to be pulled at read time. This routing decision — made per post based on the author's follower count — is the hybrid's control point.

The per-user timeline cache holds each user's precomputed feed as a capped list of recent post ids, kept in a fast store (an in-memory data structure store is typical). It is capped — you do not keep an unbounded history in the hot cache; you keep the most recent few hundred entries and fall back to recomputation or the post store for deep scrolling. This cache is what makes the common read a single fast lookup rather than a scatter-gather.

The read-time merge and ranking layer assembles the final feed. It reads the user's cached timeline (the pushed ids), pulls recent posts from the celebrities the user follows, merges the two sets, applies ranking (reverse-chron or a relevance model), deduplicates, hydrates the winning ids into full posts from the post store, and paginates. In a ranked feed this layer also mixes in signals — recency, affinity, engagement — to order the merged candidates. The diagram below shows how the post store, follower graph, fan-out service, timeline cache, and read-time merge fit together across the push and pull paths.

News feed — fan-out on write (push) vs fan-out on read (pull), hybrid for hot accountsthe core tradeoff: precompute per-follower timelines or assemble them at read timeAuthor postswrite to post store, emit eventFan-out serviceroute by follower countFollower graphwho follows whomPush (write)insert post id into each follower timeline cachePull (read)query recent posts from followees at read timeHybridpush for normal, pull for celebritiesTimeline cache (per user, capped) + ranking/merge at read + backfill from storeRead path — merge cached pushed ids with pulled celebrity posts, rank, hydrate, paginateeventlookupfewmanyhugeserve
A news feed balances fan-out on write (push a post id into every follower's cached timeline) against fan-out on read (pull recent posts from followees at query time), with a hybrid that pushes for ordinary accounts and pulls for celebrities to bound write amplification.
Advertisement

End-to-end flow

Follow a post through the hybrid. An ordinary user with eight hundred followers posts. The content is written to the post store, assigned an id, and the author immediately gets a success response — the expensive part has not happened yet. A post event is emitted onto a queue for asynchronous fan-out.

The fan-out service picks up the event, checks the author's follower count, and — being well under the celebrity threshold — chooses push. It reads the eight hundred follower ids from the follower graph and enqueues an insertion of the new post id into each of those users' timeline caches. Within seconds, every follower's precomputed timeline has the new post at its head, capped so the oldest entry falls off if the list is full. The author's post is now 'delivered' without any of those followers having done anything.

Now a celebrity with fifty million followers posts. The post is written to the store and the event is emitted identically, but when the fan-out service checks the follower count it sees the account is over the threshold and skips push entirely. The post sits in the post store, indexed by author, waiting to be pulled. One write, no amplification — the fan-out pipeline is never asked to perform fifty million insertions.

A user opens their feed. The read path fetches their timeline cache — a ready-made list of post ids pushed by all the ordinary accounts they follow — in one fast lookup. In parallel it consults the follower graph for the celebrities this user follows and pulls those celebrities' recent posts directly from the post store. Now it has two candidate sets: the pushed ordinary posts and the pulled celebrity posts.

The merge-and-rank layer unions the two sets, removes duplicates, orders them — by timestamp for a chronological feed, or by a ranking model for a relevance feed — and hydrates the top page of ids into full post content (text, media, author details) from the post store. The user sees a single coherent feed and never knows that most of it was precomputed while a slice was assembled on the fly. As they scroll, pagination fetches the next page, backfilling from the post store when the capped cache is exhausted. The end-to-end property is that ordinary posts cost their followers nothing at read time, celebrity posts cost their author nothing at write time, and the read path pays only a bounded merge — each strategy applied exactly where its cost is affordable.