Skip to content
SeeItFlow
System Design · 13 chapters · ~22 min read

Designing Twitter/X — Timeline & Feed System

A simple read-time timeline query works until an account with tens of millions of followers posts, and suddenly every one of those followers' reads is competing for the same hot rows. This walkthrough traces that breaking point into fanout-on-write, the celebrity write-amplification problem it creates, and the hybrid fanout, ranking, and caching that resolve it.

Mangesh MandlikLast updated:
Chapter 01

Problem Definition

We are designing the timeline and feed system for a social platform: users post content, follow each other, and expect a personalized, fresh feed to load instantly.

The challenge: reads massively outnumber writes. A post is created once but potentially read millions of times. And the distribution of follower counts is wildly skewed — most users have hundreds of followers, but some users have tens of millions. That asymmetry is what makes this problem hard.

Interactive

Visual Design Journey

Watch one feed architecture evolve from a naive read-time timeline into hybrid fanout, ranking, caching, and hot-partition mitigation — driven by a single celebrity post that breaks the naive design wide open.

Chapter 02

Requirements

Establish the functional and non-functional requirements before touching the architecture.

Functional: create posts, follow/unfollow users, view personalized timeline, like/reply/repost. Non-functional: very high read volume (reads >> writes), low timeline latency, support users with millions of followers, eventual consistency is acceptable for timelines.

Capacity estimate (illustrative)

These are illustrative planning numbers, not measurements of any real system. They exist to show which numbers drive the design. Assumptions: 200 million daily active users (DAU); 100 million new posts per day; each DAU opens the timeline 10 times per day; fanout-eligible authors average 200 followers (accounts above the hybrid threshold are excluded from fanout and handled at read time); peak traffic is 3× the daily average; 86,400 seconds per day.

QuantityFormulaResult
Posts per second100M ÷ 86,400 s (×3 for peak)about 1,160/s average, about 3,500/s peak
Timeline reads per second200M × 10 ÷ 86,400 s (×3 for peak)about 23,000/s average, about 69,000/s peak
Read-to-write ratio2B reads/day ÷ 100M posts/dayabout 20 : 1
Inbox writes (fanout)100M posts × 200 followers = 20B/day (upper bound: treats every post as fanout-eligible); ÷ 86,400 s (×3 for peak)about 231,000/s average, about 694,000/s peak
One celebrity post, naive fanout1 post × 50M followers50M inbox writes — about 216 s of the entire fleet's average fanout load, or 50 s even at 1M writes/s
Inbox memoryAssumed 500 post IDs per inbox × 32 B per entry (ID plus sorted-set overhead) = 16 KB; × 200M inboxesabout 3.2 TB, roughly 50 nodes at 64 GB each before replication
Post metadata growthAssumed 300 B per post × 100M posts/dayabout 30 GB/day (media excluded)
Illustrative capacity estimate — formulas use the assumptions above

What this implies for the design: writes are cheap (a few thousand posts per second) but fanout turns each one into hundreds of inbox writes, so the fanout workers and inbox store, not the Posts DB, are the write bottleneck. The 20:1 read ratio is what justifies pre-computing inboxes. A single 50-million-follower post would cost as much as several minutes of normal fanout, which is why the celebrity exclusion in the hybrid design is a requirement rather than an optimisation.

Chapter 03

Start Simple

Start with the simplest possible design: a post service, a posts database, and read-time timeline generation.

Swipe to pan
Read-Time Timeline Generation — simple writes, expensive readsUserTimeline Svcmerge & sortSocial Graphfollowing listPosts DBall users✓ Simple writesjust write the post✗ Expensive readsN DB queries per user1,000 followed accounts → 1,000 DB queries per timeline load. Unsustainable at scale.
Read-time timeline generation: simple writes, but timeline load requires N database queries per user

When a follower opens their timeline, the Timeline Service fetches their following list from the Social Graph, then fetches recent posts from each followed user. Simple to implement. Simple to reason about. Works at small scale.

Where it breaks

If a user follows 1,000 accounts, loading their timeline requires up to 1,000 database queries. Multiply by millions of concurrent users opening their timelines at peak. The Posts DB and Timeline Service saturate.

Chapter 04

Fanout-on-Write

The alternative: do the heavy lifting at write time. When a post is created, push it to every follower's personal timeline inbox immediately.

Swipe to pan
Fanout-on-Write — push posts to follower inboxes at write timeUsercreates postFanout Queueasync jobsWorker× NWorker× NWorker× NInboxper-user RedisResult: timeline read = O(1) — just fetch your inboxTrade-off: write amplification = O(followers). Fine for 500. Catastrophic for 50M.
Fanout-on-write: post creation triggers async workers that push the post ID into each follower's inbox — timeline read cost no longer depends on how many accounts the user follows

The fanout process is asynchronous. The user who created the post doesn't wait for it. Workers pick jobs off a queue and write the post ID into each follower's inbox — a per-user, pre-built list of recent post IDs.

Timeline inbox

A per-user data structure (typically in Redis) that stores the post IDs of the last N posts from accounts the user follows. Loading the timeline is trivial: fetch the inbox, hydrate the post details from the Posts DB.

Write amplification

The cost of fanout-on-write is O(followers). For a user with 500 followers, one post creates 500 inbox writes. This is manageable. But it grows linearly — and for celebrity users, it becomes catastrophic.

Swipe to pan
Write Amplification — fanout cost scales with follower countNormal · 100100 writesPopular · 10K10,000 writesFamous · 1M1,000,000 writes⭐ Celebrity · 50M50,000,000 writes50M writes from one post → queue overwhelmed → all other posts delayed
Write amplification: 500 followers = fine; 50 million followers = 50 million inbox writes from one post
Key takeaways
  1. 01Timeline read cost is bounded and independent of follow count — just fetch the pre-built inbox
  2. 02Write cost is O(followers per post creator)
  3. 03Fanout is asynchronous — users don't wait for it
  4. 04Acceptable for users with small-to-moderate follower counts
  5. 05Catastrophic for users with millions of followers
Chapter 05

The Celebrity Problem

A celebrity with 50 million followers creates one post. Fanout-on-write must now create 50 million inbox writes.

Swipe to pan
The Celebrity Problem — write amplification at 50M followersNormal UserUser500 followersFanout500 writesInboxes500 × write✓ ManageableCelebrity⭐ Celebrity50M followers🔥 FanoutOVERWHELMED50M jobs queuedInboxes50M writes✗ Catastrophichours to drain1 post → 50,000,000 inbox writes. All other users' posts delayed while the queue drains.
The celebrity problem: one post → 50M inbox writes → queue overwhelmed → all other users' posts delayed

A naive design enqueues one job per follower, so a single post from a 50-million-follower account puts tens of millions of jobs on the fanout queue at once. Workers are overwhelmed. The queue backlog grows. And while the system drains this celebrity post backlog, all other users' posts are stuck in the queue too — their followers see delayed timelines even for normal posts.

A single celebrity post has become a denial-of-service event for the entire fanout system.

Chapter 06

Hybrid Fanout

The solution: use different fanout strategies for different users based on their follower count.

Swipe to pan
Hybrid Fanout — different strategy for different usersWRITE TIMENormal User500 followers⭐ Celebrity50M followersFanout → Inbox500 writesPosts DB only0 fanoutO(500) writes ✓O(0) fanout ✓READ TIME — compose from bothFollowerreadingInboxnormal postsPosts DBcelebrity postsComposermerge + rankTimelinefinal feed
Hybrid fanout: normal users get fanout-on-write; celebrities get fanout-on-read; timeline is composed from both at read time

Normal users use fanout-on-write. Their posts are pushed to follower inboxes asynchronously. Accounts above a high follower-count threshold are excluded from fanout — their posts sit in the Posts DB and are fetched at read time instead.

Timeline composition

When a follower opens their timeline, the Timeline Composer: (1) fetches their pre-built inbox from normal user fanout, (2) fetches recent posts from celebrity accounts they follow directly from the Posts DB. The number of celebrities a typical user follows is small — so this read-time fetch is bounded. The two sets are merged, ranked, and returned.

Threshold

The cutoff is a tunable follower-count threshold, not a fixed number to claim as fact. Set it from the fanout capacity: with the illustrative numbers in the Requirements chapter (about 231,000 inbox writes per second on average), one account's post must stay a small fraction of the queue and worker budget, and the threshold should sit well below the point where a single post would saturate it. It also depends on the read-time cost of the opposite choice, since every follower of an excluded account pays an extra fetch on each timeline open. Accounts near the boundary can move across it as their follower counts change.

Chapter 07

Timeline Ranking

A chronological feed isn't the best experience. A ranking service scores candidate posts to produce a personalized, relevant feed.

Swipe to pan
Timeline Ranking — scoring candidate posts before servingInbox Postsfrom fanout⭐ Celeb Postsfrom DBCandidatesmerged pool🎯 Ranking Svcscore & sortFeedrankedSignals: recency · engagement · relationship strength · content relevance · media type
Ranking pipeline: candidate posts from inbox + celebrity fetch → scoring engine → ranked timeline

The timeline is never ranked from every post in the system — that would be both wasteful and unbounded. Candidate posts are first collected from available sources (the precomputed inbox and any pull sources), then filtered and have their features retrieved, and only then does ranking order that bounded pool: candidate sources → filtering → feature retrieval → ranking → final feed.

Signals include recency, engagement metrics (likes, replies, reposts), relationship strength (how closely connected the reader is to the author), content relevance based on the reader's history, and media type preferences.

Separation of concerns

Ranking is deliberately decoupled from candidate collection. The fanout system collects candidates; the ranking system orders them. This lets both evolve independently — you can ship a new ranking model without touching the fanout infrastructure, and vice versa.

The full multi-stage pipeline — how candidates are actually generated and merged, how features are hydrated, and how scoring stages are structured — is covered in Architecture Deep Dive → Candidate Generation & Feed Composition and → Ranking Pipeline.

Chapter 08

Timeline Caching

Active users check their timelines many times a day. Recomputing the full timeline on every request is wasteful — cache it.

Swipe to pan
Timeline Caching — cache hits in microseconds, miss falls back to compositionUserCache HIT⚡ Timeline CacheRedis · per-usermicroseconds · no DBCache MISSMissnot cachedComposeinbox + celeb⚡ Store result→ cachecomposed → cached → served
Cache hit: served from memory, no DB touch. Cache miss: full composition → cache → serve

The timeline cache (Redis-like) stores the composed, ranked timeline per user. Cache hit: return from memory with no database touch (a networked cache typically answers in about a millisecond or less). Cache miss: run full composition — inbox fetch + celebrity fetch + ranking — store the result, return it.

"Timeline caching" is really shorthand here — a production feed has distinct cache layers for post/object data, timeline/inbox candidates, and the final composed/ranked result, each with different freshness needs. See Architecture Deep Dive → Multi-Layer Feed Caching for how those layers differ.

Cache freshness

A cached timeline introduces staleness: a new post might not appear until the TTL expires or the cache is explicitly invalidated. Given that eventual consistency is acceptable, a short TTL — for illustration, on the order of tens of seconds, as in the Deep Dive — is typically fine. Users accept that their feed isn't real-time to the second.

Chapter 09

Hot Partitions

A skewed access pattern concentrates an enormous amount of traffic on one storage partition or key. Adding generic capacity doesn't automatically redistribute a hot key — the load stays concentrated unless the underlying skew itself is addressed.

Swipe to pan
Hot Partition — uneven load that adding machines alone cannot fixUsersviral eventPartition A30%Partition B🔥 HOT KEY97%Partition C25%Mitigations⚡ Cache absorbs 94%+ of readsRead replicas spread DB loadKey sharding for extreme cases✗ Adding machines ≠ fixHot key still lands on same partition
Hot partition: viral traffic concentrates on one shard — adding machines doesn't help because the hot key still lands on the same node

This shows up as skewed access to a small number of keys or shards rather than evenly spread load: an extremely popular post being read by millions at once, celebrity-related metadata being looked up constantly, a disproportionately active shard, a high-degree social-graph node, or a cache-miss storm concentrating reads onto a subset of partitions right when the cache can least absorb it.

Why more machines don't help

The hot traffic targets a specific set of keys — whichever partition those keys happen to land on stays saturated. Adding more partitions doesn't move existing keys without an explicit resharding operation, so generic horizontal scaling alone doesn't relieve a genuinely hot key; it just adds idle capacity elsewhere.

Mitigations

First line: the cache absorbs the overwhelming majority of reads, so only misses reach the storage layer at all. Second: read replicas on hot partitions spread read load across multiple nodes serving the same shard. Third, for genuinely extreme hot keys: explicit key/data sharding — splitting a single high-traffic key's data across multiple nodes. If load is still unmanageable, load shedding protects the rest of the system at the cost of some requests.

Chapter 10

Failure Scenarios

At scale, each component will fail. The architecture must degrade gracefully rather than catastrophically.

Fanout workers fail

New posts are still stored in the Posts DB. The fanout queue grows. Follower timelines become stale — they stop receiving new posts in their inboxes. When workers recover, they drain the backlog. No data is lost. Impact: temporarily stale timelines.

Timeline cache fails

All reads fall through to full composition. The Timeline Composer and ranking service handle elevated load. Timelines are correct but slower. When the cache recovers, it warms up organically as requests come in.

Ranking service unavailable

Fall back to a simpler ordering strategy: reverse chronological. Timeline is still served, just not personalized. This is a graceful degradation — less ideal but fully functional.

Celebrity post

Hybrid fanout prevents write amplification. The celebrity's post lands in the Posts DB. Read-time fetch handles delivery to followers. The fanout system is largely unaffected on the write path.

Chapter 11

Core Trade-offs

Every decision in this design involves an explicit trade-off. Name them.

Fanout-on-write

Fast reads. Expensive writes. Problematic for users with very large follower counts. Best for: users with small-to-moderate follower counts where write amplification is bounded.

Fanout-on-read

Cheap writes. Expensive reads. More computation at timeline request time. Best for: celebrity users where write amplification would be catastrophic.

Hybrid

Best of both worlds at the cost of architectural complexity. The threshold between strategies is a tunable parameter. Requires handling transitions when users cross the threshold.

Chronological vs ranked feed

Chronological is simple, predictable, and transparent to users. Ranked improves relevance and engagement but introduces algorithmic opacity and requires significant ML infrastructure.

Chapter 12

Feed Correctness Beyond New Posts

So far the story has mostly modeled create post → distribute post → read feed. That's not the whole picture — feed entries can become invalid after fanout has already happened.

A post already sitting in a follower's inbox can stop being valid to serve for several reasons: the post is deleted, the author blocks the reader (or the reader blocks the author), the account is suspended or made private, the reader unfollows the author, a repost is removed, or visibility changes in some other way. None of these are exotic edge cases — at scale, some combination of them is happening constantly.

The cleanup algorithms and full serving-time mechanics live in Architecture Deep Dive → Post Lifecycle, Deletion & Visibility — this section exists just to establish that the problem is real before the final architecture is assembled.

Chapter 13

Final Architecture

The complete architecture, assembled from first principles.

Swipe to pan
Final Architecture — hybrid fanout, ranking, caching, hot-partition mitigationUserposts⭐ CelebritypostsPost Servicewrite to DBPosts DBall postsQueueasyncFanout Workersnormal onlyInboxnormal postsFollowerreads feed⚡ CachetimelineComposerinbox + celeb🎯 Rankingscore & sortFinal Feeduser sees✓ Hybrid fanout · Ranking · Cache · Hot-partition mitigation via cache + read replicas
The complete system: hybrid fanout workers, timeline inbox, Posts DB for celebrities, composer, ranking, cache, and hot-partition mitigation

Write path: User posts → Post Service → Posts DB (source of truth). Fanout job enqueued. Workers consult the Social Graph for follower lookup: normal-strategy users → inbox writes; pull-strategy users → skipped. Read path: Timeline request → cache → candidate collection (inbox + pull sources) → visibility/filtering → feature hydration → ranking → final feed. The Social Graph is also consulted directly on the read path wherever pull-source discovery needs a reader's followed-account list, not only during fanout.