Social media feed architecture.
Fan-out on write is the right answer until someone with ten million followers posts, and the entire design exists to handle that exception.
The components
Every row below is read from the graph that produced the diagram above, so the two cannot disagree.
| Component | Tier | Why it is there |
|---|---|---|
| Client | Client | Supporting component |
| API gateway | Edge | Supporting component |
| Post service | Application | Supporting component |
| Social graph | Application | Follower edges |
| Fan-out worker | Application | Push on write, except for celebrities |
| Feed service | Application | Merges pushed timeline with pulled hot accounts |
| Ranking | Application | Supporting component |
| Kafka | Data | Supporting component |
| Redis timelines | Data | Materialised per user |
| Cassandra | Data | Supporting component |
| Object store | Data | Supporting component |
Design decisions worth arguing about
A diagram shows what was chosen. It does not show what it cost, and that is usually the part that matters in a review or an interview.
Fan-out on write makes reads cheap and writes expensive
Pushing each post into every follower's timeline at publish time means reading a feed is a single range query, which is what you want for the operation that happens constantly. The cost is proportional to follower count, so an average user is trivial and a popular one is a million writes for a single action. That asymmetry, not the average case, is what dictates the architecture.
A pull path for the accounts that break it
Above some follower threshold, fan-out stops being viable, so those accounts are not pushed at all. The feed service instead fetches their recent posts at read time and merges. This makes reads more expensive for followers of popular accounts, which is most people, and it is still cheaper than the alternative. The complexity cost is real: two code paths producing one feed, and a threshold that needs tuning.
Timelines are a cache, not a record
Materialised timelines are derived data and should be rebuildable from posts and the graph. Treating them as authoritative means a bug corrupts something you cannot regenerate. Keeping them disposable lets you cap their length, expire inactive users entirely and rebuild on demand, which bounds memory in a way that storing every user's full timeline never could.
Ranking after retrieval, not instead of it
Ranking a candidate set you already have is fast; ranking by querying a model for every possible post is not. Retrieving chronologically and then reordering keeps the expensive part bounded. The consequence is that anything not retrieved cannot be ranked, so retrieval quality caps ranking quality, which is the same lesson as retrieval-augmented generation in different clothes.
How it changes with scale
Fan-out volume grows with the product of users and average follower count, which grows faster than either. Timeline storage grows with active users and is controlled by capping length and expiring inactive accounts. The read path is comparatively flat, which is the entire return on doing the work at write time.
Where it breaks first
A celebrity post during a spike. Even with a pull threshold, the accounts just under it produce enormous fan-out bursts that saturate the workers and delay every other user's timeline. The symptom is not an error but posts appearing minutes late for people unrelated to the event, which is why fan-out lag is a metric worth alerting on.
Draw this yourself
Open the Diagram tab and describe the system. The agent emits a semantic graph rather than coordinates, so you can edit the components and the layout re-solves instead of drifting.
When the shape is right, Implement in code turns the canvas into a markdown specification, every component, every relationship and the notes, and starts a real turn in the Code tab with it.
Questions about this design
Why not fan-out on read for everyone?
Because it makes the most frequent operation the most expensive one: assembling a feed means querying every followed account on every refresh. It is simpler and it does not survive real usage patterns.
Where should the follower threshold be?
Wherever fan-out latency starts affecting other users, which is a property of your worker capacity rather than a universal number. It is a tuning knob that should be adjustable without a deploy.
How are deletes handled?
By filtering at read time rather than removing from every materialised timeline, which would be another fan-out. The timeline holds ids, and a deleted post is skipped when hydrated.
Does a chronological feed simplify this?
It removes the ranking service and nothing else. The fan-out problem, the celebrity problem and the storage problem are all identical, which is why chronological feeds are not meaningfully cheaper to build.
More templates
- Web Crawler System DesignThe engineering problem is not fetching pages quickly. It is fetching them politely, at scale, without visiting the same content a thousand times.
- Search Autocomplete System DesignThis is a latency problem wearing a search problem's clothes: past about a hundred milliseconds users have already typed the next character and your response is worthless.
- File Storage Service DesignThe design goal is to keep file bytes away from your servers entirely, so that what you run is a metadata service that happens to be about files.
- Ticket Booking System DesignA named seat can be sold exactly once, and ten thousand people want it at the same instant. Everything here follows from that.
Last updated