Twitter / X#
Problem statement (interviewer prompt)
Design Twitter/X: users post 280-char tweets, follow each other, and see a home feed of tweets from people they follow (chronological + For-You). Support 500M tweets/day, 5B reads/day, hashtag search, trends, likes, retweets, replies, and DMs.
flowchart LR
U([User])
TW[Tweet Service]
FAN[[Fan-out]]
TL[(Home Timelines<br/>Redis ZSET)]
TWDB[(Tweet DB)]
GRAPH[(Social Graph)]
HOME[Home Timeline API]
SEARCH[Search]
U -->|post| TW --> TWDB
TW --> FAN
GRAPH --> FAN
FAN --> TL
U -->|read| HOME --> TL
TWDB -. index .-> SEARCH
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class U client;
class TW,HOME,SEARCH service;
class TWDB datastore;
class TL cache;
class FAN queue;
flowchart TB
subgraph Clients
iOS[iOS]
AND[Android]
WEB([Web])
end
subgraph Edge
DNS[DNS]
CDN[CDN media + assets]
LB[L7 LB]
GW[API Gateway / GraphQL]
WAF[WAF + Bot]
end
subgraph Write[Write Path]
TWS[Tweet Service]
MS[Media Service]
OBJ[(S3 / blob)]
ID[Snowflake ID]
INGEST[[Kafka tweet stream]]
end
subgraph Graph[Social Graph]
FG[Follow Service<br/>FlockDB / TAO-like]
GDB[(Graph store)]
end
subgraph FanOut[Fanout - Hybrid]
FANW[[Fanout workers]]
CELEB([Celebrity classifier])
PUSH[[Push fanout<br/>writes per follower]]
PULL[Pull on read<br/>for celeb followees]
HOMECACHE[(Home Timeline cache<br/>Redis ZSET per user)]
end
subgraph Storage
TDB[(Tweets store<br/>Manhattan / Cassandra)]
USERDB[(User store)]
LIKES[(Likes / Retweets KV)]
TRENDS[(Trends KV)]
end
subgraph Read[Read Path]
HOME[Home Timeline API]
USER([User Timeline API])
HYD([Hydrator<br/>tweet + author + counts])
RANK([ML Ranker<br/>For-You])
DEDUP[Dedup + filter]
end
subgraph Search[Search / Trends]
EARLY[Earlybird<br/>real-time inverted index]
SPARK([Spark / Heron streams])
QPARSE([Query parser])
end
subgraph Media
TRANS([Transcoder])
THUMB[Thumbnailer]
LIVE[Live streaming<br/>Periscope-style]
end
subgraph Notif
NS[Notification Service]
PUSHN((APNS / FCM))
end
subgraph ML
REC([Recommendation<br/>Who-to-follow])
SCORE[Scoring service]
SAFE[Trust & Safety<br/>spam / abuse models]
end
subgraph Obs
MET[Metrics]
TRC[Trace]
LOG[Logs]
end
Clients --> DNS --> CDN --> LB --> WAF --> GW
GW --> TWS
TWS --> ID
TWS --> TDB
TWS --> INGEST
TWS --> MS --> OBJ
MS --> TRANS --> THUMB
INGEST --> FANW
FG --> FANW
CELEB --> FANW
FANW -->|normal user| PUSH --> HOMECACHE
FANW -->|celeb author| PULL
GW --> HOME --> HOMECACHE
HOME --> PULL --> TDB
HOME --> HYD --> RANK --> DEDUP --> Clients
USER --> TDB
INGEST --> EARLY
GW --> Search --> EARLY
INGEST --> SPARK --> TRENDS
TWS --> LIKES
INGEST --> NS --> PUSHN
SAFE -.filter.-> RANK
REC -.suggestions.-> Clients
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class WEB,USER client;
class DNS,CDN,LB,GW,WAF edge;
class iOS,AND,TWS,MS,PULL,HOME,DEDUP,THUMB,LIVE,NS,SCORE,SAFE service;
class ID,FG,GDB,TDB,USERDB,LIKES,TRENDS,EARLY datastore;
class HOMECACHE cache;
class INGEST,FANW,PUSH queue;
class CELEB,HYD,RANK,SPARK,QPARSE,TRANS,REC compute;
class OBJ storage;
class PUSHN external;
class MET,TRC,LOG obs;
Hybrid fan-out#
- Normal user (<10k followers): fan-out push to timeline cache.
- Celebrity (>1M followers): no fan-out; pull at read for that subset.
- Merger interleaves push-cached tweets with celeb tweets by timestamp.
Storage#
- Tweets: Manhattan (Twitter's KV) -
tweet_id PK, denormalized counts. - Likes/RTs: counters (TAO-style or Redis HLL for view counts).
- Search: Earlybird = Lucene-based real-time inverted index, segmented by time.
Ranking#
- For-You feed = candidates from network + recommendations → heavy DNN scorer.
- Latency budget per scorer < 10 ms.
Live tweets#
- Read-after-write: route subsequent reads to coordinator that committed.
- Push to active sessions over Streaming API (Server-Sent Events / WS).
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Load balancer / GSLB | L4/L7 traffic distribution and failover | load-balancer |
HLD |
CDN | edge caching for static assets | cdn |
HLD |
API gateway / BFF | single ingress, auth, rate limit, routing | api-gateway |
HLD |
Pub/Sub & message brokers | topics, consumer groups, delivery semantics | pub-sub-pattern |
HLD |
Probabilistic data structures | Bloom, HLL, Count-Min, MinHash, t-digest | probabilistic-data-structures |
HLD |
Observability | metrics, logs, traces, SLOs | observability |
HLD |
Realtime protocols | WS / SSE / polling / gRPC streaming | realtime-protocols |
HLD |
Search internals | inverted index, BM25, embeddings, ANN | search-internals |
Quick reference#
Functional#
- Post tweet (text/media, ≤ 280 chars).
- Follow / unfollow.
- Home timeline (chronological + For-You).
- Search, hashtag/trend, mentions.
- Like, retweet, reply, DM.
- Notifications.
Non-functional#
- 500M tweets/day, 5B reads/day.
- p99 home timeline open < 200 ms.
- 99.99% availability.
Capacity#
- 500M tweets/day → 5,800/s avg, 50k/s peak.
- 5B reads/day → 58k/s avg, 500k/s peak.
- Average follower count ~200; fan-out write = 500M × 200 = 100B/day = 1M/s.
- Tweet storage: 500M × 365 × 1 KB = 180 TB/yr text + media in S3.
Schema#
tweets(id PK, author_id, text, created_at, lang, media_ids[])follow(follower_id, followee_id, created_at)(sharded by follower_id and by followee_id - both directions).home_timeline(user_id, [(ts, tweet_id), ...])- Redis ZSET, capped 800.likes(tweet_id, user_id); counts in Redis.
Trade-offs#
- Push wins for read latency but explodes for celebs.
- Pull-only simpler but adds latency and load to author shards.
- Hybrid is the production answer; merger adds complexity.
- Ranked feed (ML) drives engagement but at compute cost and feedback-loop hazards.
Refs#
- "The infrastructure behind Twitter" engineering blog, FlockDB paper, Manhattan KV blog, Earlybird real-time search paper, ByteByteGo "Design Twitter", Alex Xu Vol 2.
FAQ#
How does Twitter generate the home timeline?#
Fan-out on write pushes each tweet into Redis ZSETs of followers at publish time. Reading the timeline is then a single ZRANGE call, hitting p99 under 100 ms.
How does Twitter handle celebrity accounts?#
Celebrities skip fan-out and their tweets are merged at read time. The reader joins their pushed timeline with fresh tweets from a small set of followed celebrities.
Why use fan-out on write instead of on read?#
Read traffic dominates by 100x. Doing the work at write time gives the hot path a single cache call instead of expensive cross-shard scatter-gather on every feed open.
How is tweet storage sharded?#
Tweets are stored in an LSM-tree KV like Manhattan or RocksDB, sharded by tweet ID. The social graph and timeline caches live in separate fleets tuned to their access patterns.
How does Twitter search find tweets so fast?#
Earlybird inverted indexes are sharded by time. Recent tweets sit in memory, older ones tier to disk. A query fans out across shards, merges results, and ranks them.
Further reading#
Curated, high-credibility sources for going deeper on this topic.
- ✍️ Blog - Twitter Engineering - The infrastructure behind Twitter: scale
- ✍️ Blog - Twitter Manhattan - real-time multi-tenant distributed database
- 📄 Paper - Earlybird - real-time search at Twitter (ICDE '12)
- 🎥 Talk - Twitter Timeline at Scale (InfoQ)