SYSTEM DESIGN · DESIGN PROBLEMS · FEEDS AND SOCIAL · MEDIUM · 12 OF 41

Design Twitter: Tweets, Home Timelines, Real-Time Search and Trends

A full walkthrough of a short-post platform like Twitter or X: requirements, the read-heavy scale math, time-sortable IDs, the tweet and timeline APIs, user versus home timelines, hybrid fan-out in a timeline cache, the celebrity problem, near real-time search with an inverted index, trending topics with windowed counts, and the trade-offs to name.

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
1 / 17

Press play to hear the narration, or step through at your own pace.

Notes

Listen

Requirements. Post a tweet of up to two hundred eighty characters, maybe with a media reference. Follow people. See two timelines: the home timeline, built from everyone you follow, and a user timeline, one person's own tweets. Search recent tweets and see trending topics. Direct messages, ads, audio rooms and editing are out of scope for today.

Qualities. The home timeline should return in under two hundred milliseconds. A tweet should reach followers within a few seconds and be searchable within about ten. Reads dominate. Eventual consistency is fine: nobody minds seeing a tweet two seconds late. And traffic is spiky: a final match or an election can multiply tweets per second several times in a minute, so plan headroom.

Functional requirements
  • in scope:
  • • post a tweet (280 chars,
  • optional media reference)
  • • follow users
  • • home timeline (followees)
  • • user timeline (one author)
  • • search recent tweets
  • • trending topics
  • out: DMs, ads, spaces, edit
Non-functional requirements
  • home timeline p99 under 200 ms
  • tweet visible to followers
  • within ~5 seconds
  • searchable within ~10 seconds
  • very read heavy
  • highly available, eventual
  • consistency is fine
  • spiky: big events = 5x traffic

Ask scale

Assume two hundred fifty million daily users and five hundred million tweets a day. That's about five thousand tweets a second on average, and five times that during a big event. Each user loads the timeline twenty times a day: five billion loads, fifty thousand a second, a hundred fifty thousand at peak. With around two hundred followers each, every tweet fans out to roughly two hundred timelines.

Storage. A tweet with its metadata is about a kilobyte, so five hundred gigabytes a day, about a hundred eighty terabytes a year before replication. Media lives in object storage behind a CDN, a separate problem. And the timeline cache is small: eight hundred tweet IDs of eight bytes for two hundred fifty million active users is about one point six terabytes. A Redis cluster holds that comfortably.

Traffic
DAU                 250M
tweets/day          500M
  500M / 100,000 s = 5K wps
  big-event peak   = 25K+ wps

timeline loads      20/user/day
  5B / 100,000 s   = 50K rps
  peak             = 150K rps

read:write          ~10 to 100:1
avg followers       ~200
Storage
tweet: text 280 chars UTF-8
  + ids, ts, counts  ~ 1 KB
500M x 1 KB      = 500 GB/day
  x 365          = ~180 TB/yr
  x 3 replicas   = ~550 TB/yr

timeline cache: 800 IDs x 8 B
  x 250M active  = 1.6 TB

media: separate blob store

Define API

The API. Post a tweet with text, optional media IDs and an optional reply-to. Two timeline reads, home and user, both paginated with max ID: give me twenty tweets older than this ID. That works because tweet IDs sort by time. Search takes a query and a since ID for polling new results. Trends take a place. And follow is a simple POST. Writes carry an idempotency key so a retry never posts twice.

Core API
POST /v1/tweets
  { text, mediaIds[], replyTo? }
  -> { tweetId }
GET  /v1/timeline/home
     ?max_id=..&count=20
GET  /v1/users/{id}/tweets
     ?max_id=..&count=20
GET  /v1/search?q=..&since_id=..
GET  /v1/trends?place=..
POST /v1/users/{id}/follow

Data model

Data model, starting with IDs. Use a sixty-four-bit ID with a millisecond timestamp in the high bits, then a worker number and a sequence, the scheme Twitter published as Snowflake. IDs are unique without coordination and sort by time, so newest-first is just descending ID order. Tweets are sharded by tweet ID, the user timeline is an index by author, follows are stored in both directions, and the home timeline is a capped Redis list per user.

Records
tweetId 64-bit:
  41 bits ms time | 10 bits
  worker | 12 bits sequence
tweet  (tweetId PK, authorId,
        text, replyTo, ts)
user_tl authorId -> [tweetId]
follow (follower, followee)
       stored both directions
home_tl userId -> Redis list
counts tweetId -> likes, RTs

High-level design

The write path. The tweet service gets an ID, persists the tweet, and publishes an event. Fan-out workers read the author's followers from the graph and push the tweet ID onto each follower's home timeline list in Redis, trimming the list to eight hundred so memory stays bounded. The client got its success response right after the persist; fan-out happens in the background.

The read path. The timeline service reads the first twenty IDs from your Redis list, multi-gets the tweets from a tweet cache in front of the store, adds author info and counts, and returns. That's one list read and one batched lookup. Notice the same event stream also feeds the search indexer and the trends counter, so each consumer scales independently and a slow indexer never delays timelines.

Deep dive: timelines

Deep dive one: timelines and the celebrity problem. A tweet from an account with fifty million followers would mean fifty million list pushes, which takes minutes and swamps the workers just when everyone wants that tweet. So above a follower threshold we don't push. At read time, the timeline service takes your precomputed list and merges in the recent tweets of the few celebrities you follow, which are hot in cache. Sort by ID, which is time order, and return.

A few edge cases interviewers like. Don't fan out to users who haven't logged in for a month; rebuild their timeline when they return. On unfollow, filter that author out at read time and clean the list lazily, instead of scanning it immediately. Deletes leave a tombstone in the tweet store, so hydration simply skips the deleted ID. The cached list is a hint; the store is the truth.

Hybrid fan-out
  • push: authors under ~100K
  • followers, done in seconds
  • pull: authors over threshold
  • read their user timeline
  • (hot, cached) at request
  • home = merge(my list,
  • recent tweets of the celebs
  • I follow), sort by ID
Timeline edge cases
  • inactive users: no push if
  • idle 30 days; rebuild on
  • login from followees
  • unfollow: filter at read,
  • lazy cleanup of the list
  • delete: tombstone in store;
  • hydrate skips it
  • replies: show only if I
  • follow both people

Deep dive: search

Deep dive two: search. An indexer consumes the stream, breaks each tweet into terms, hashtags and mentions, and appends the tweet ID to each term's posting list in an inverted index. Partition the index by time: one hot segment takes all new writes and is searched first, older segments are read-only. A query fans out to the shards, takes the best results from each, and merges them by recency and engagement. Twitter has described a system like this, called Earlybird.

Real-time search
  • tokenise: words, hashtags,
  • mentions, language
  • inverted index:
  • term -> [tweetId newest first]
  • shard by time: the hot shard
  • takes all new writes
  • query: fan out to shards,
  • merge top-K by recency +
  • engagement
  • index lag target ~10 s

Deep dive: trends

Deep dive three: trends. A stream processor counts hashtags and phrases in one-minute buckets per region. Exact counts for every phrase are too expensive, so use a count-min sketch for approximate counts and a heap for the top candidates. Crucially, trending isn't the same as popular: score each term by how much its current rate exceeds its usual rate, otherwise common words always win. Filter spam and refresh every minute or so.

Trending topics
  • stream job counts terms per
  • 1-min bucket, per region
  • approximate counts:
  • count-min sketch + top-K heap
  • trend score = recent rate vs
  • normal rate for that term
  • ("weather" is always high)
  • filter spam, refresh ~1 min

Trade-offs

Trade-offs and failures. If fan-out falls behind, timelines lag, but the durable stream means no tweet is lost. If a Redis node dies, rebuild those lists by pulling from followees. A viral tweet is a hot key in the tweet cache, so replicate it to several nodes. During a big event, autoscale workers and you can lower the celebrity threshold on the fly. Watch fan-out lag, p ninety-nine timeline latency, search index lag and cache hit rate.

Say the trade-offs out loud. Pushing costs memory and write throughput but makes reads one cache hit. Pulling for celebrities costs a little read latency but keeps writes sane. Eventual consistency buys availability. At ten times the scale, add regions, shard Redis by user ID, keep fewer IDs per list, and move old tweets to cheaper storage since almost nobody reads them.

Failures and monitoring
  • fan-out backlog: timelines
  • lag, queue keeps the work
  • Redis node lost: rebuild
  • lists by pull, slower once
  • hot tweet: replicate its key
  • in the tweet cache
  • big event: autoscale fan-out,
  • raise celeb threshold live
  • watch: fan-out lag, p99 read,
  • index lag, cache hit rate
Trade-offs to name
  • push: memory + write load
  • for cheap reads
  • pull for celebs: some read
  • latency for sane writes
  • eventual consistency for
  • availability
  • 10x: more regions, shard
  • Redis by userId, keep only
  • ~400 IDs, tier old tweets

Say it like this

The summary. Tweets get time-sortable sixty-four-bit IDs and are stored sharded by ID. After persisting, an event drives three consumers: fan-out into capped Redis home timelines, a time-partitioned inverted index for search, and a windowed counter for trends. Celebrities are pulled and merged at read time. Everything downstream is eventually consistent, which is exactly what this product needs.

The 60-second answer
  • time-sorted 64-bit IDs
  • tweet store sharded by ID
  • event stream after persist
  • fan-out IDs into Redis home
  • lists, capped at 800
  • celebs: pull + merge at read
  • search: time-partitioned
  • inverted index off stream
  • trends: windowed sketch,
  • rate vs baseline

Interview questions

What is the difference between the home timeline and the user timeline?

The user timeline is one author’s own tweets, a simple index by author ID. The home timeline merges tweets from everyone the viewer follows, which is why it needs fan-out or a merge at read time.

Why use time-sortable IDs for tweets?

A sixty-four-bit ID with a timestamp in the high bits is unique without central coordination and sorts by creation time. Timelines and pagination become range reads on the ID, such as twenty tweets older than a given max ID.

How do you handle accounts with tens of millions of followers?

Skip fan-out for accounts above a follower threshold and, when a user loads their home timeline, merge their cached list with the recent tweets of the celebrities they follow. Those celebrity timelines are few and very hot, so they stay in cache.

How do you make new tweets searchable within seconds?

An indexer consumes the tweet event stream and appends each tweet ID to the posting lists of its terms in an in-memory, time-partitioned inverted index. Queries hit the newest partition first and merge results across shards.

How would you compute trending topics?

A stream job counts terms in short time buckets per region using a count-min sketch and keeps a top-K heap of candidates. A term trends when its current rate is far above its normal rate, not merely when its count is high.

How much memory does the timeline cache need?

With eight hundred eight-byte IDs per user and two hundred fifty million active users, about one point six terabytes before replication. Storing only IDs, not tweet bodies, is what keeps it this small.

What happens to timelines when someone deletes a tweet?

The tweet is marked deleted in the store, and hydration drops any ID that no longer resolves, so it disappears immediately even though the ID may still sit in cached lists. A background job can clean those lists later.

How do you survive a sudden spike during a major event?

The durable event stream absorbs the burst, fan-out workers autoscale on lag, and the celebrity threshold can be lowered so fewer tweets are pushed. Reads stay fast because they hit the timeline and tweet caches.