What the interviewer is asking
"Design Twitter" (now X) is one of the classic system design questions. The hard part isn't storing tweets. It's the home timeline: the latest tweets from everyone you follow, newest first, different for every person and opened many times a day.
The question checks whether you can find the busy path, size it with rough numbers, and decide where the work happens: when a tweet is written, or when a timeline is read. The walkthrough below follows the 8 steps of the interview framework: scope, data, API, the busy path, numbers, services, scale and deep dives.
1. Scope: what version 1 must do
Agree on the smallest version that is still useful. Three features make the loop Twitter runs on:
- Post a tweet: text, up to 280 characters.
- Follow someone.
- Read your home timeline: tweets from the people you follow, newest first.
Name the rest out loud and park it: like and retweet counts, search, trending topics, notifications, photos and videos. Each is a system of its own (a counter, an index of words, a stream job, a push service, file storage with a CDN), and the timeline works without any of them.
2. Data: users, tweets and follows
Three kinds of records:
user id, name, handle
tweet id, author_id, text, time
follow follower_id → followee_id
A tweet stores its author's id, not a copy of the author's details. A follow is one record per pair: "Ana follows Ben" is follower Ana, followee Ben. The design reads follows both ways: who Ana follows, to build her timeline, and who follows Cy, to deliver Cy's tweets. Index both columns.
Tweet ids that grow with time (the Snowflake-style ids of the Unique IDs lesson) help here: sorting by id is sorting by time, which is exactly what "newest first" needs.
3. The API: three calls
POST /tweets {: "Hello, world"} → 201 Created {: "t6"}
POST /follows {: "cy"} → 201 Created
GET /timeline?cursor=… → 200 OK 20 tweets, newest first, and the next cursor
A timeline can hold thousands of tweets, so it comes in pages of 20. Ask for the next page with a cursor, the id of the last tweet the reader saw ("give me the ones older than this"), not a page number. With page=2, new tweets arriving on top push everything down, so the reader sees tweet 20 again as tweet 21. A cursor doesn't move when new tweets arrive. The Pagination lesson covers it in depth.
4. The busy path: reading the home timeline
People open their timeline many times a day and post far less, so the read is the path that has to stay fast. In the simplest design, one server and one database, every read does this:
- Look up who the reader follows.
- Read the latest tweets of each of them.
- Merge them, newest first.
- Send back the first 20 and a cursor.
That's one lookup per person followed, on every open. Ana follows 3 people; plenty of people follow hundreds.
5. The numbers
Numbers from the interviewer: 200 million daily users, each opening the timeline about 10 times a day and posting about 0.5 tweets a day. A day has 86,400 seconds; round it to 100,000 for quick math.
- Reads: 200 million × 10 = 2 billion a day, ÷ 100,000 ≈ 20,000 a second (about 23,000 with the exact 86,400).
- Tweets: 200 million × 0.5 = 100 million a day ≈ 1,000 a second (about 1,200 exactly).
- Busy hours: about 5× the average, so plan for 100,000 reads and 5,000 tweets a second.
- The ratio: 20 timeline reads for every tweet. That one number drives the design.
- Storage: 100 million tweets a day at about 500 bytes each is about 50 GB a day, about 18 TB a year.
6. Services: a write side and a read side
One server taking 100,000 reads and 5,000 tweets a second means a rush of readers slows down posting, and you can't add servers for just one side. Split it:
- Tweet service: checks and saves new tweets, and saves follows (a small, rare write).
- Timeline service: builds and returns home timelines.
Each side now grows with its own traffic: a few servers for the writes, many for the reads.
7. Scale: fan-out on read or fan-out on write?
This is the decision the whole question is about. Run both ways on a tiny Twitter of 5 people and 8 follows:
Ana follows Ben, Cy, Dee
Ben follows Cy, Eli
Dee follows Cy
Eli follows Cy, Dee
Cy follows no one
Fan-out on read builds a timeline when someone asks for it. Ana's read fetches the tweets of Ben, Cy and Dee (3 fetches) and merges them. If each of the 5 opens the timeline 10 times a day, that's 8 follows × 10 = 80 fetches and 50 merges.
Fan-out on write does the work when a tweet is posted. Each person has a ready list of tweet ids in a cache, and a new tweet's id is pushed onto the front of each follower's list. When Cy posts, her 4 followers get the id: 4 writes. If each of the 5 posts once a day, that's 8 pushes, and each of the 50 reads is a single lookup.
The same 8 follows cost 80 fetches one way and 8 pushes the other, because people read 10 times for every tweet. At Twitter's size, say each person follows about 200 accounts. Every follow has one follower and one followee, so on average each person also has about 200 followers:
- Fan-out on read: 20,000 reads × 200 = 4 million fetches a second, and each read waits for its slowest list.
- Fan-out on write: 1,000 tweets × 200 = 200,000 pushes a second, and each read is one lookup.
Fan-out on write does 20× less work and moves it off the busy path. A cache in front of the tweets doesn't change that math: each fetch gets faster, but a read still makes about 200 of them.
How big is the timeline cache?
The cache keeps tweet ids, not tweets: say the latest 800 per person, 8 bytes each. For 200 million daily users that's 200 million × 800 × 8 bytes ≈ 1.3 TB. Copying whole tweets (about 500 bytes) into every timeline would take about 80 TB; each tweet is stored once and fetched by id.
Say one cache server holds about 100 GB: that's about 13 servers, so the cache is split by user id (sharding). Each person's list sits whole on one server, so a read stays one lookup. At about 100,000 operations a second each, 13 servers also take the busy-hour load of about 1 million pushes and 100,000 reads a second.
A read then fetches the 20 tweets on screen by id, in one batch, usually from a cache of recent tweets. People who haven't opened the app in weeks don't need a cached timeline: drop it, and build it on demand with fan-out on read if they come back. Scrolling past the 800th tweet falls back to the database the same way.
8. Deep dive: the fan-out queue
If the tweet service makes every push before it answers, posting gets slow. At about 0.5 ms for each round trip, 200 followers take 100 ms, and an account with 100,000 followers would wait 50 seconds.
Instead, save the tweet, put a "new tweet" message on a queue, and answer right away. Fan-out workers take messages off the queue, look up the author's followers and push the id into each timeline. If a worker crashes halfway, the message is still on the queue for another one. Pushing only to followers who are online would leave holes in everyone else's timeline; the pushes are needed, the poster just shouldn't wait for them.
The queue is also where the parked features plug in later: search indexing and notifications can read the same new-tweet messages. That's publish/subscribe, and a log-based stream such as Kafka is a common choice for it.
9. Deep dive: celebrities
Fan-out on write breaks for huge accounts. A celebrity with 100 million followers makes 100 million pushes for every tweet: as many as 500,000 ordinary tweets (100 million ÷ 200), or more than 8 minutes of everyone's tweets at 1,000 a second, landing at once. Her followers at the end of the line can see the tweet minutes late.
The fix is a hybrid. Keep pushing tweets from normal accounts. For the few accounts above a threshold, say a million followers, don't push at all: when someone reads, the timeline service takes their ready list and merges in the latest tweets of the celebrities they follow. Someone who follows 200 accounts, 3 of them celebrities, makes 4 lookups per read instead of 200, and a celebrity's tweet is stored once instead of pushed 100 million times.
Twitter has described this kind of design: fan-out on write into in-memory timelines, with very large accounts handled apart.
What breaks first
- The tweets database: 50 GB a day grows to terabytes, so shard it. By tweet id spreads writes and storage evenly but scatters one person's tweets across shards; by user id keeps a person's tweets together but makes a celebrity's shard hot.
- Celebrity tweets on the read path: millions of readers fetch the same few tweets, so keep them in a cache, with copies on several servers.
- The fan-out backlog: about 1 million pushes a second at busy hours. Add workers; the queue absorbs the bursts.
- A timeline cache server going down: its timelines can be rebuilt from the database with fan-out on read, or served from a replica.
- Unfollows and deleted tweets: remove the ids from the cached lists, or filter them out at read time.
Summary: the order to follow
- Scope: post, follow, read your home timeline; park likes, search, trends, notifications and media.
- Data: users, tweets (id, author, text, time) and follows (follower → followee).
- API:
POST /tweets,POST /follows,GET /timeline?cursor=…. - Busy path: reading the home timeline.
- Numbers: about 20,000 reads and 1,000 tweets a second; 100,000 and 5,000 at busy hours.
- Services: a tweet service and a timeline service.
- Scale: fan-out on write into a timeline cache of ids, about 1.3 TB, split by user id.
- Deep dives: a queue and fan-out workers so posting answers fast; celebrities' tweets pulled at read time and merged.
The same steps work for any feed, from Instagram to a news feed. The question that decides the design is always the same: does the work happen when something is written, or when it is read?