Design Twitter / News Feed
Every timeline read has to feel instant for 200M daily users. The interview turns on where you pay for fan-out: when a tweet is written, or when a timeline is read.
1 · Requirements
The Prompt
Design a Twitter-like timeline: 500M users, average 200 follows, p95 timeline read < 200ms.
Functional
- Post a tweet with text and optional media
- Read a home timeline of followed accounts, newest first, with paging
- Follow and unfollow accounts
- Search tweets by keyword
Non-Functional
- Timeline reads return in under 200 ms at p95
- New tweets reach followers' timelines within a few seconds
- Accounts with millions of followers never stall the write path
2 · Estimates
Drill This Phase First
Show the Answer and Estimates
Answer
About 920,000 a second
400M tweets a day is about 4,600 a second. Each one fans out to 200 timelines, so 4,600 times 200 is about 920,000 inserts a second.
- Writes
- 200M daily users posting 2 tweets a day: 400M tweets, about 4,600 a second and 12,000 at peak
- Reads
- 50 timeline loads per user a day: 10B reads, about 115,000 a second and 290,000 at peak
- Fan-Out
- 200 followers per tweet on average: about 920,000 timeline inserts a second on the push path
- Storage
- About 1 KB a tweet: roughly 150 TB a year in a wide-column store
- Cache
- 800 tweet IDs per timeline at 16 bytes each: about 2.5 TB across a Redis cluster
- Celebrities
- Accounts over about 10,000 followers, roughly 0.1% of users, get pulled at read time to cap fan-out
3 · API
POST /tweets { text, media_ids? } → { tweet_id, created_at }
GET /timeline?cursor=… → { tweets[], next_cursor }
POST /follow { target_user_id } → 204
GET /users/:id/tweets?cursor=… → { tweets[], next_cursor }
GET /search?q=… → { tweets[] } (search-index path)Data Model
tweets (wide-column, partition = user_id, cluster = tweet_id DESC):
tweet_id (PK) : snowflake
user_id : uuid
text : string (<=280)
media_ids : list<uuid>
created_at : timestamp
timelines (wide-column, partition = user_id, cluster = ts DESC):
user_id (PK) : uuid
ts : timestamp
tweet_id : snowflake
author_id : uuid
follows (KV, partition = follower_id):
follower_id : uuid
followee_id : uuid
created_at : timestamp4 · High-Level Design
5 · Deep Dives
Push, Pull, or Hybrid
Push writes each tweet into every follower's cached timeline, so a read is one Redis lookup. That breaks for accounts with millions of followers, where one tweet means millions of writes. The hybrid pushes for normal accounts and pulls celebrity tweets at read time, merging the two lists before returning the page.
Timeline Cache Shape
Store only tweet IDs, capped at about 800 per user, and hydrate the tweet bodies in one batch read. Inactive users can fall out of the cache and get a rebuilt timeline on their next visit, which keeps the 2.5 TB estimate from growing with total signups.
Cursor Paging
Page with a cursor built from the last tweet ID. Snowflake IDs sort by time, so the cursor stays stable while new tweets arrive. Offset paging skips or repeats tweets whenever the timeline grows between page loads.
6 · Wrap Up
The Short Version
Fan-out-on-write for normal users (push to followers' timeline cache), fan-out-on-read for celebrities (pull at read). Wide-column store for tweets.