Twitter/X timeline
Timeline generation with hybrid fan-out — push for regular users, pull for celebrities.
The scenario
The archetypal feed system — hybrid fanout, hot celebrities, and the 143,199 TPS record
Same startup, same engineer #4. Fourth Monday of the year.
Your CTO drops by your desk with coffee. "Marketing loves the URL shortener and Slack, and Instagram is in beta. Now they want something more ambitious — a public microblogging feature. Users post short text and media. Followers see them in a reverse-chronological timeline. Handles like @jane. Direct messages. Ship an MVP in 12 weeks."
She pauses. "And by the way — one of our early beta users has 8 million followers. She just tweeted, and our L4 architecture melted."
That's the whole game.
Here's the pattern you've been building toward:
URL Shortener was read-heavy with tiny fanout (redirect happens 1:1).
Slack was real-time write-heavy but bounded fanout (channel of ~100 members).
Instagram shifted the paradigm — the ranking became the product, and follows became one signal among 50.
Twitter is the archetypal fanout problem. Every tweet from a regular user with 200 followers = 200 timeline writes. Every tweet from a celebrity with 50 million followers = 50 million writes. The push-fanout that works for 99% of users breaks catastrophically for 1% who drive 90% of engagement. Understanding this cliff — and how Twitter engineered around it — is the whole 12 chapters.
The real 2024 numbers
Before we design anything, know the real scale you're designing against:
- ~610M MAU (Reuters, Feb 2025), down from 250M+ DAU pre-2022
- ~500M tweets/day at peak (2013 peak was 500M/day, ~5,700 TPS average)
- Record peak: 143,199 TPS — set during a "Castle in the Sky" broadcast in Japan, 2013 (Twitter Engineering blog, 2013)
- Recent peak: 24,400 TPS — 2022 FIFA World Cup Final (Twitter Engineering, 2022)
- Top account (as of 2024): @elonmusk with 200M+ followers; @BarackObama with 130M+ (Wikipedia list)
- Timeline reads: ~10 billion/day at peak (2015)
- Read:write ratio: ~1000:1
Why 143,199 TPS is the interview soundbite: it was NOT sustained — it was a peak from a synchronized cultural event where an entire country tweeted the same word at the same second. Twitter's steady-state is much lower. Real interview greatness = knowing that number AND knowing when to say "but the sustained rate is closer to 6K TPS."
The whole journey at a glance
Every 10× in follower count forces a fundamentally different fanout strategy:
text═══════════ TWITTER ARCHITECTURE ACROSS 4 SCALES ═══════════ L4 (10K users) L5 (10M users) L6 (500M users) L7 (X-scale, all-time) max ~10K followers max ~100K follwrs max ~50M followers max ~200M (Musk) 12 weeks · $500/mo 6 months · $50K/mo 18 months · $500K/mo ongoing · $1B+/yr ┌────────┐ ┌────────┐ ┌── Global CDN ──────┐ ┌── Own CDN + AI ────┐ │ Web │ │ Web │ │ timeline + media │ │ media + Grok LLM │ │ Mobile │ │ Mobile │ │ edge caches │ │ edge inference │ └───┬────┘ └───┬────┘ └─┬──┬──┬──┬─────────┘ └──┬──┬──┬──┬──────┘ │ │ │ │ │ │ │ │ │ │ ┌──▼───┐ ┌──▼──┐ ┌──▼──▼──▼──▼─────┐ ┌──▼──▼──▼──▼──────┐ │ ALB │ │ ALB │ │ Multi-region │ │ 20+ regions + │ │ HTTPS│ │ WSS │ │ ALB · anycast │ │ anycast + BGP │ └──┬───┘ │ HTTPS│ └───┬──────┬──────┘ └────┬──────┬──────┘ │ └──┬──┘ │ │ │ │ ┌──▼──────┐ ┌───▼─────────┐ ┌──▼──────▼─────┐ ┌─────▼──────▼──────┐ │ 1 API │ │ API pods │ │ Regional │ │ Hybrid fanout │ │ server │ │ sharded by │ │ API + │ │ service: │ │ │ │ user_id │ │ hybrid │ │ push < 10K │ └──┬──────┘ │ │ │ fanout │ │ pull > 10K │ │ └───┬─────────┘ │ service │ │ celebrity model │ │ │ └───┬───────────┘ └──┬──┬──┬──┬───────┘ │ ┌───▼──┐ ┌───▼──────────┐ │ │ │ │ │ │Redis │ │Redis Cluster │ ┌──▼──▼──▼──▼─────┐ │ │timeln│ │timeline cache│ │Timeline Service │ │ │cache │ │per-region │ │(the OLD Manhattan) │ └───┬──┘ └───┬──────────┘ │+ RedisCluster │ │ │ │ └──┬──────────────┘ ┌──▼───┐ ┌───▼─┐ ┌──▼──────────┐ │ │MySQL │ │MySQL│ │Sharded MySQL│ ┌──▼──────────────┐ │(user+│ │+ 2 │ │+ Manhattan │ │Manhattan (2016+)│ │ tweet│ │repl │ │Twitter's KV │ │KV store built at│ │+follow │icas │ │store (retire│ │Twitter 2014-16 │ │Multi-│ │+ Kaf│ │d 2022 for X)│ │+ HDFS + Kafka + │ │AZ) │ │ka │ └─────────────┘ │ Flink for search │ └──────┘ └─────┘ └─────────────────┘ ↑ ↑ ↑ ↑ Boring Fan-out on write Hybrid: push for regular Multi-region. pull-based for regular users. followers, pull-merge Real-time search. JOIN works. Kafka for celeb for celebrities on read. Grok LLM inference events. Manhattan KV store is compute-bound, + Redis Cluster. not I/O bound. Chapter 5 Chapters 6+6.5 Chapter 7+7.5 Chapter 8 walks walks through walks through walks through through push-fanout + the hybrid model, X's post-2022 L4 pull Redis timeline Manhattan history, reorg + Grok + timeline in cache and the celebrity multi-region full cliff at 10K followers economics Key insight: 99% of Twitter users are fine with push fanout. 1% (celebrities) destroy the model. Twitter's ANSWER: HYBRID. Push for followers < 10K, pull-merge celebrity tweets at read time. That's the entire architecture story.
The same 4 tiers as clean architecture diagrams
The ASCII compressed all four tiers. Here are the same shapes as clean Mermaid — the kind an interviewer expects on a whiteboard:
L4 · 10K users · pull-based JOIN · $500/mo · 12 weeks:
flowchart TD
W([Web / Mobile clients]) -->|HTTPS| LB[ALB<br/>$25/mo]
LB --> API[1 API server<br/>c5.large<br/>~1K RPS timeline]
API --> PG[(MySQL Multi-AZ<br/>users + tweets + follows<br/>$150/mo)]
API -->|presigned PUT| S3[(S3<br/>media binaries)]
classDef n fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
class LB,API,PG,S3 nL5 · 10M users · push-fanout for everyone · $50K/mo · 6 months:
flowchart TD
W([Web / Mobile]) -->|HTTPS + WSS| LB[ALB · WebSocket for realtime]
LB --> R{User-affinity<br/>router<br/>hash by user_id}
R --> A1[API 1]
R --> A2[API 2]
R --> A3[API 3]
A1 --> KF[Kafka<br/>tweet-created events]
KF --> FW[Fanout workers<br/>write to N follower timelines]
FW --> RD[(Redis timeline cache<br/>list of tweet_ids per user)]
A1 --> RD
A2 --> RD
A3 --> RD
A1 --> PG[(MySQL primary<br/>+ 2 read replicas<br/>tweets + follows source-of-truth)]
classDef n fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef c fill:#fef3c7,stroke:#d97706,color:#78350f
class LB,R,A1,A2,A3 n
class KF,FW,RD,PG cL6 · 500M users · hybrid push/pull · $500K/mo · 18 months:
flowchart TD
W([Global users]) -->|HTTPS anycast| CDN[Global CDN]
CDN --> LB[Regional ALB<br/>us-east · eu-west · ap-northeast]
LB --> API[Regional API pods]
API --> HFS[Hybrid Fanout Service<br/>push if follower < 10K<br/>pull-merge celebs on read]
HFS --> RC[(Redis Cluster<br/>per-region timeline cache<br/>tweet_ids for 500M users)]
HFS --> MAN[(Manhattan KV store<br/>Twitter's built-2014<br/>tweet metadata)]
HFS --> C[(Cassandra tier<br/>social graph shards)]
API --> ES[Elasticsearch<br/>real-time search index]
API --> KFA[Kafka + Flink<br/>ML signals + search index]
classDef n fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef m fill:#fce7f3,stroke:#be185d,color:#831843
class CDN,LB,API,RC n
class HFS,MAN,C,ES,KFA mL7 · 610M+ MAU · X-scale + Grok LLM + custom silicon · $1B+/yr:
flowchart TD
W([Global users]) -->|HTTPS anycast| CDN[X's Own CDN<br/>edge caches + media + Grok]
CDN --> LB[20+ regional ALBs]
LB --> API[Regional API]
API --> HFS[Hybrid Fanout Service]
HFS --> GROK[Grok LLM inference<br/>reply suggest · search · rank<br/>on H100/H200 GPUs]
HFS --> RC[(Redis Cluster<br/>timeline cache)]
HFS --> MAN[(Manhattan KV<br/>tweet + user metadata)]
API --> ES[Search index<br/>Elasticsearch/OpenSearch]
API --> SC[Streaming compute<br/>Kafka + Flink<br/>ranking signals<br/>trending topics]
API --> C[(Cassandra multi-region<br/>replicated 3x)]
classDef n fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef m fill:#fce7f3,stroke:#be185d,color:#831843
classDef ml fill:#dcfce7,stroke:#16a34a,color:#14532d
class CDN,LB,API n
class RC,MAN,C,ES m
class HFS,GROK,SC mlWhy every 10× breaks the architecture
Look at the four tiers side by side. Two patterns dominate:
- Storage stays boring. MySQL → Sharded MySQL → Manhattan + Cassandra. Boring engineering, and it's fine.
- Fanout mutates. L4 pull → L5 push → L6 hybrid → L7 hybrid + ML rerank. The fanout strategy IS the architecture.
The celebrity cliff: at ~10K followers, push-fanout starts costing more than pull-merge on read. Twitter engineered a hybrid ~2012: push if followers < 10K, pull-merge everyone else's celebrity tweets at read time. Reference: Twitter Engineering: "The Infrastructure Behind Twitter: Scale".
Why not push everything? At Elon's 200M followers, a single tweet = 200M timeline writes. Even at 10K writes/sec/node = 20K node-seconds = 5.5 hours to fully fan out. Pull-merge on read is O(N) reads at 200M × ~5% online = 10M timeline requests over a few hours. Cheaper, faster, correct.
Why not pull everything? At L5 with 10M users each following 200 users, every timeline read = 200 tweet_id fetches = 200 Redis calls. Redis handles this, but ballooned reads at billions per day = massive Redis costs. Push is dramatically cheaper for the 99% of users with < 10K followers.
The 3 senior insights before we start Chapter 1
- The problem is NOT storage — it's the fanout curve. Every candidate memorizes "we shard MySQL." That's fine. What Twitter engineered is the decision boundary between push and pull, which is more interesting than any DB choice.
- Manhattan is NOT Cassandra. Twitter built its own KV store (Manhattan) in 2014 for tweet metadata — because Cassandra's eventual-consistency semantics didn't fit their SLA. Reference: Twitter Engineering: "Manhattan, our real-time, multi-tenant distributed database". Do NOT say "Twitter uses Cassandra for tweets" — that's a common tutorial-parrot mistake.
- The 143K TPS record is a peak, not a baseline. Baseline is ~6K TPS. Interviewers love asking "how do you handle 143K TPS?" — the right answer is "we don't design for that; we design for the 24K TPS World Cup peak (sustained ~30 min) and let Kafka queue absorb the microsecond bursts."
Chapter map for the journey ahead
- Chapter 1 — Requirements (functional + non-functional per level)
- Chapter 2 — Capacity estimation (610M MAU, 500M tweets/day, 10B timeline reads/day)
- Chapter 3 — API design (tweet, timeline, follow, DM, search)
- Chapter 4 — Data model (users, tweets, follows — and the graph problem)
- Chapter 4.5 — The follows graph: adjacency list vs graph DB vs materialized fanout
- Chapter 5 — L4 MVP: pull-based JOIN. Works up to ~10K users
- Chapter 6 — L5: push-fanout with Kafka + Redis
- Chapter 6.5 — The celebrity cliff. Hybrid push/pull decision matrix
- Chapter 7 — L6: Manhattan KV store, Redis Cluster, multi-region
- Chapter 7.5 — The tweet ranking + search pipeline (Elasticsearch + Flink)
- Chapter 8 — L7: X-scale + Grok + real-time ML reranking
- Chapter 9 — Failure modes: celebrity death spiral, timeline cache stampede, follow-graph inconsistency
- Chapter 10 — Trade-off matrix
- Chapter 11 — Interview masterclass: 45-min mock, questions to ask
- Chapter 12 — Defense: the 20 hardest interview questions on Twitter
Ready? Chapter 1 next: what did the CTO actually ask for?
Twitter/X at 610M MAU is a fanout problem, not a storage problem. 99% of users work with push-fanout; 1% (celebrities) require pull-merge on read. The hybrid decision (push if followers < 10K, else pull) is Twitter's architectural signature — and Manhattan (not Cassandra) is the KV store that made it work. The 143,199 TPS peak is a soundbite; steady-state is 6K TPS.
- Why is Twitter fundamentally a fanout problem, not a storage problem?
- What is the celebrity cliff and where is it (10K followers)?
- What's the difference between push, pull, and hybrid fanout?
- What is Manhattan and why did Twitter build it instead of using Cassandra?
- What's the difference between 143K TPS peak and 6K TPS steady-state?
Every concept below has its own interactive, animated page in the Learning Tracks section. Read them any time you want to go deeper than the mentor prose above — they're the reusable foundation this chapter is built on.
The algorithm behind Manhattan's shard placement and Redis Cluster slot assignment at L6.
The backbone of Twitter's fanout pipeline — every tweet becomes a Kafka event that workers consume to write follower timelines.
Chapter 1 next: what did the CTO actually ask for? Post a tweet, read a timeline, follow, DM — each has functional and non-functional requirements. Get these wrong and you'll design the wrong system for the whole 12 chapters.
Components used in this design
Study each component's deep dive to understand it in isolation.
Load Balancer
Distributes incoming traffic across a pool of servers for scale and fault tolerance.
CDN (Content Delivery Network)
A globally distributed cache that serves static and cacheable dynamic content close to the user.
Redis
An in-memory key-value store used for caching, pub/sub, rate limiting, distributed locks, and simple queues.
SQL Database
A row-oriented, ACID-compliant relational database — the default for transactional workloads.
NoSQL Database
Umbrella for document, wide-column, and key-value stores optimized for horizontal scale over strict schema.
Kafka
A distributed, partitioned, replicated commit log for event streaming, high-throughput ingest, and decoupled services.
API Gateway
Single entry point that handles auth, rate limiting, routing, and protocol translation for downstream services.
Object Storage (S3, GCS, Azure Blob)
Durable, cheap, flat-namespace storage for blobs — images, videos, backups, logs, and any large binary object.
Search Engine (Elasticsearch, OpenSearch, Meilisearch)
Inverted-index-based full-text and structured search — the right tool for 'find me records matching this query' when SQL LIKE isn't fast enough.
Rate Limiter
Enforces per-caller (per-user, per-IP, per-tenant) request budgets to protect downstream systems from abuse and overload.
Stream Processor (Flink, Kafka Streams, Spark Structured Streaming)
Continuous computation over event streams — windowed aggregations, joins, ML features, and event-driven derived views.
Service Discovery (Consul, etcd, DNS-based)
How services find each other in a dynamic fleet — because IPs change, nodes come and go, and hardcoding is a losing game.
Workflow Engine (Temporal, Cadence, AWS Step Functions)
Orchestrates long-running, stateful multi-step processes — the right tool when a business flow involves many services and can span days.
Patterns applied in this design
Study each pattern's deep dive for the recurring solution logic.
Cache-aside (Lazy loading)
Reads dominate your workload, and every read hits a slow store (database, disk, network). Latency and load on the primary storage climb until the primary becomes the bottleneck.
Circuit breaker
A downstream service is unhealthy. Your service keeps calling it, waiting the full timeout on every call, exhausting your thread pool, and cascading the failure back to your callers.
Sharding (Horizontal partitioning)
Your dataset or write throughput has outgrown a single node. A single primary DB or single cache node can't hold the data or handle the QPS.
Consistent hashing
You need to shard keys across N nodes, but N changes over time (nodes added, removed, or failed). Naive hash-mod-N reshuffles almost everything on every change — cache is wiped, migration cost is huge.
Event sourcing
Traditional state-based storage loses history: you know the current balance, but not why it's that value. Auditing, replay, and temporal queries are hard.
CQRS (Command Query Responsibility Segregation)
Your write model and read model have very different needs. Optimizing for one hurts the other.
Rate limiting
One bad actor can consume all your capacity. And even good actors need bounds so you can capacity-plan.
Explore next — related systems
If you enjoyed this problem, these share similar patterns or challenges.
Photo/video feed, uploads, stories, direct-to-S3 media, hybrid fan-out feed, ML ranking.
OpenNews Feed
Ranked, personalized feed generation.
OpenNotification Platform
Multi-tenant SaaS notification platform — templates, providers, orchestration, delivery receipts.
Open