Fan-out-on-write versus fan-out-on-read
coreadvancedA feed is a join between "who I follow" and "what they posted", and you can pay for that join at write time or at read time. Fan-out-on-write pushes each new post into a precomputed list for every follower at the moment it is published, so opening the app is one cheap read of an already-assembled list. Fan-out-on-read stores each post once and assembles the feed when someone asks for it, by fetching the recent posts of everyone they follow and merging. The trade is direct: fan-out-on-write makes reads fast and writes expensive and proportional to follower count, while fan-out-on-read makes writes trivial and reads expensive and proportional to following count. Which is right depends on the ratio between the two, and for most social products reads dominate heavily, which argues for fan-out-on-write. The problem is that write cost is not evenly distributed. A user with fifty followers costs fifty list insertions; a user with fifty million costs fifty million, all triggered by one tap, and that skew — usually called the celebrity problem — is what makes the pure form of either approach unworkable at scale. Real systems use a hybrid: fan out on write for ordinary accounts, do not fan out posts from very large accounts at all, and merge those in at read time. Each user's feed is then one cheap read of their materialised list plus a small merge from the handful of large accounts they follow.
Think of it as
Two ways to run a newsletter. You can address and post an envelope to every subscriber the moment you write something, which makes reading effortless — the letter is already on the doormat — and makes publishing proportional to your subscriber list. Or you can pin the letter to a noticeboard and let each subscriber walk round every noticeboard they care about, which makes publishing free and reading a chore that grows with how many boards someone follows. Neither works for a writer with fifty million subscribers and neither works for a reader who follows two thousand boards, which is why real systems post envelopes for ordinary writers and keep noticeboards for the famous ones.
What we're doing: Cost the same two posts under both strategies, then under the hybrid.
- 6
- This is the number that kills pure fan-out-on-write. The cost is not the total volume — it is that one user action produces it, so the write burst has no natural smoothing and the queue behind it becomes the feature's latency.
- 15
- Pure fan-out-on-read fails from the other direction, and it fails on every read rather than on rare writes. Eight hundred reads per refresh is the kind of cost that looks acceptable in a prototype and does not survive a real user base.
- 23
- The hybrid bounds both sides: write cost can never exceed the threshold, and read cost is bounded by how many large accounts one person follows, which is small in practice because there are not many such accounts.
Why this works: Costing both strategies against the two extreme cases shows that neither pure form is a design so much as one half of one. The threshold is the actual design decision, and it is tunable: raise it and reads get cheaper while celebrity writes get more expensive, lower it and the reverse.
Fanning out synchronously inside the publish request
Wrong
Better
What you see: Publish latency scales with follower count, so posting is instant for new users and takes tens of seconds for popular ones. Under load the publish endpoint's connection pool is exhausted by long-running fan-out loops, and posting fails for everyone.
Why: Fan-out is bounded by audience size, which is a property of the author rather than of the request, so putting it on the request path makes latency depend on something the user cannot influence and the system cannot cap. Making the post durable and fanning out asynchronously gives a constant-time publish and turns propagation delay into queue lag, which is measurable and shed-able.
- Fan-out-on-write
- One post → one insertion per follower, asynchronously
- Opening the feed is a single ordered range read
- Feed lists are derived data and must be rebuildable
- Breaks on skew: one celebrity post is millions of writes
- Fan-out-on-read
- One post → one insertion, regardless of audience
- Opening the feed reads every followed account and merges
- No duplicated storage, no propagation lag
- Breaks on breadth: a user following thousands is a slow read
The two strategies, side by side
Why the hybrid is the usual answer
Remember: A feed is a join between follows and posts, paid for either at write time (fan-out-on-write: cheap reads, writes proportional to follower count) or at read time (fan-out-on-read: cheap writes, reads proportional to following count). Reads dominate, so fan-out-on-write is the default — but follower counts are skewed, and one celebrity post is millions of writes from one tap. The working answer is a hybrid: fan out below a follower threshold, merge large accounts in at read time, run fan-out asynchronously, and keep posts and follows authoritative so materialised feeds stay rebuildable.
See also: caching ranking pagination and materialization · timeline materialization and cache invalidation · choosing shard keys · why components disagree · decoupling with queues

