Design problems
Social, feeds & contentHardSDE-2 35 min

Design Twitter / X

Asked at
GoogleLinkedInMetaShareChat

The interviewer asks

“Design Twitter. Users post short tweets, follow other users, and read a home timeline. Also support search over tweets and trending topics.”

What a strong answer looks like

A strong answer covers four subsystems and keeps each simple: tweet storage (Snowflake IDs, sharded by tweet ID, cached heavily), the home timeline (hybrid fan-out: push tweet IDs into Redis timelines for normal users, merge celebrity tweets at read time), search (near-real-time inverted index fed from Kafka, partitioned by time and tweet ID) and trends (streaming counts of hashtags/terms in sliding windows, compared to a baseline to find what is spiking). Reads dominate writes ~100:1, so the design is read-optimised. Numbers: ~250M DAU, ~6k tweets/s average with spikes of 50k+/s during big events, ~300k timeline reads/s. Interviewers expect the timeline fan-out maths, the celebrity problem, and a clear story for search and trends.

Concepts to mention

Read-heavy, precomputed timelinesHybrid fan-out with celebrity thresholdRedis timelines of tweet IDsSnowflake IDs and cursor paginationHydration from cacheKafka as the tweet event busReal-time inverted index with time tieringScatter-gather searchTrend detection by velocity vs baselineCount-Min Sketch and top-K

1 Requirements

Functional

  • Post a tweet (up to 280 characters, optional media), reply, retweet and like
  • Follow and unfollow users
  • Home timeline: tweets and retweets from people you follow, newest or ranked
  • User timeline: all tweets by one user
  • Search tweets by keyword and hashtag, newest results within seconds
  • Trending topics globally and per country/city

Non-functional

  • Scale: 500M MAU, 250M DAU, 500M tweets/day
  • Home timeline latency: p99 < 200 ms server side
  • Tweet visible to followers within ~5 s (eventual consistency is fine)
  • Search freshness: new tweets searchable within ~10 s
  • Availability: 99.99% for reading; writes can briefly degrade
  • Handle event spikes: 10x normal tweet rate during a World Cup goal or election result

Out of scope (say it out loud)

  • Direct messages
  • Ads and monetisation
  • Media transcoding pipeline in detail

2 Estimates

Tweet writes~6k/s (peak ~60k/s)500M tweets/day ÷ 86,400 ≈ 5,800/s; big moments spike ~10x
Timeline reads~300k/s peak250M DAU × ~30 timeline refreshes/day ÷ 86,400 ≈ 87k/s average; ×3-4 at peak
Fan-out inserts~1.2M/s average5,800 tweets/s × ~200 median followers reached (celebrities excluded)
Tweet storage~150 GB/day text + metadata500M × ~300 bytes ≈ 150 GB/day ≈ 55 TB/year before replication; media goes to object storage
Timeline cache~1.6 TB in Redis250M active users × 800 tweet IDs × 8 bytes ≈ 1.6 TB; with Redis overhead and replicas ~5 TB RAM
Search index growth~1 TB/day500M tweets × ~2 KB of postings and stored fields per tweet ≈ 1 TB/day; recent days on SSD, older on cheaper tiers

3 API

EndpointWhat it does
POST/2/tweetsCreate a tweet: text, media_ids, reply_to; returns tweet_id
GET/2/timeline/home?cursor=&count=50Home timeline page using a max_id-style cursor
GET/2/users/{id}/tweets?cursor=User timeline
POST/2/users/{id}/followingFollow a user
GET/2/search?q=&cursor=Search recent tweets by keyword or hashtag
GET/2/trends?woeid=Trending topics for a location

4 Data model

tweetsSharded MySQL / Manhattan-style KV + Memcached
tweet_id (PK, Snowflake 64-bit)user_idtextmedia_idsreply_toretweet_ofcreated_at

Shard by tweet_id for even spread. Snowflake IDs embed time, so sorting by ID = sorting by time; no separate created_at index needed for timelines.

user_tweetsCassandra / wide-column
user_id (partition)tweet_id (clustering, desc)

User timeline is a single partition read; also the source for celebrity pull.

followsSharded graph store (FlockDB-like) on MySQL
(follower_id, followee_id, created_at); stored in both directions

Forward list for 'who do I follow', reverse list for fan-out. Follower counts kept separately.

home_timelineRedis Cluster
tl:{user_id} → list of (tweet_id, author_id, retweet flag)capped at ~800

Only IDs. Replicated 3x. Built for active users only; rebuilt on demand.

search_indexLucene-based real-time index (Elasticsearch-like, Earlybird-style)
term → posting list of tweet_ids; stored fields: user_idlangengagement

Partitioned by tweet_id hash, tiered by time: in-memory segments for the last hours, SSD for older.

trend_countsFlink state + Redis
(location, term, minute bucket) → count

Sliding-window counts and a longer baseline for spike detection; top-K per location written to Redis.

5 Architecture

CLIENTSEDGESERVICESASYNCDATApost tweethome timelinesearch, trendsstoreTweetCreatedfollowerspush tweet_idread IDshydrate, celeb pullqueryMobile / WebAPI GatewayTweet ServiceTimeline ServiceSearch ServiceKafka (tweet events)Fan-out WorkersSearch IndexersTrends (Flink)Tweet Store +MemcachedFollow Graph StoreTimeline Cache(Redis)Search Index
ClientEdge / gatewayService / workerQueue / streamCacheDatabase / storageAI / modelThird partyrequestasync (event / queue)

Hover a box to highlight its connections.

6 Request flows

Post a tweet

  1. 1Client sends POST /tweets with an idempotency key; Tweet Service validates length and runs spam checks
  2. 2Tweet Service generates a Snowflake ID (timestamp + worker ID + sequence), writes the tweet and the user_tweets row, and caches it
  3. 3It publishes TweetCreated to Kafka (partitioned by author_id) and returns 201
  4. 4Fan-out workers read the author's follower list; if followers < threshold (e.g. 100k), they LPUSH the tweet ID to each active follower's Redis timeline and LTRIM to 800
  5. 5Search indexers add the tweet to the in-memory segment of its index partition; it is searchable within seconds
  6. 6Trends job tokenises hashtags and phrases and increments per-location counts in the current 1-minute window

Read the home timeline

  1. 1Timeline Service reads the user's cached ID list from Redis (one call, < 1 ms)
  2. 2It gets the list of celebrities the user follows (cached) and reads their latest tweet IDs from user_tweets
  3. 3It merges the lists by tweet ID (which is time order), removes duplicates and blocked/muted authors
  4. 4It hydrates tweets, authors and engagement counts with batched multi-gets from Memcached
  5. 5Optionally a ranking model reorders the top few hundred (for the 'For you' tab)
  6. 6Returns 50 tweets with a cursor = smallest tweet_id returned, so the next page asks for tweet_id < cursor

7 Deep dives

Timeline fan-out and the celebrity problem

The home timeline is the heart of the design. With reads ~100x writes, we precompute timelines: when a user tweets, push the tweet ID into each follower's Redis list. This is the approach Twitter described publicly: timelines in Redis, about 800 entries each, replicated 3 times. But an account with 100M followers would need 100M Redis writes per tweet, which takes minutes and wastes work on inactive followers. So we use a threshold: accounts above ~100k followers are not fanned out. Their tweets are pulled at read time from their user timeline and merged in. Because a user follows few such accounts, the merge is a handful of extra cache reads. Further tuning: only fan out to users active in the last ~30 days, rebuild a returning user's timeline by pull, and process fan-out for followers who are online first. Retweets are fanned out like tweets but with a 'retweet of' reference so the original is hydrated once. Fan-out is idempotent (dedupe by tweet ID) so Kafka replays are safe.

Search: real-time inverted index

Users expect a tweet to be searchable seconds after posting, so a nightly batch index is not enough. Indexers consume TweetCreated from Kafka, tokenise text (with language detection, because Indian Twitter mixes Hindi, Hinglish and English), and append to an in-memory index segment. Twitter's Earlybird design used an index optimised for appends where newest documents are searched first, because most queries want recent tweets. The index is partitioned by tweet ID hash across many shards, and each shard is replicated for read throughput. A query fans out to all partitions of the recent tier (scatter-gather), each returns its top results, and a broker merges them. Time tiering keeps the hot tier small: the last few days in RAM/SSD, older tweets in an archive tier queried only if needed. Ranking combines text relevance, recency, author reputation and engagement. Deletes are handled by a deleted-ID set checked at query time, then merged out during segment compaction.

Trending topics

Trending is not 'most frequent', otherwise 'good morning' trends every day. It is 'growing much faster than usual'. A stream processor (Flink) reads tweets, extracts hashtags and n-grams, and counts them per location in 1-minute buckets. For each term it keeps a short window count (last 15 minutes) and a baseline (same hour over past days, or an exponentially decayed average). The trend score is roughly the ratio of current rate to expected rate, with a minimum volume and a boost for many distinct users (so one bot farm cannot trend a tag). Counting every term exactly is costly, so use a Count-Min Sketch plus a heap for top-K candidates, then exact counts only for candidates. Per location (India, Mumbai, Bengaluru) the top 30 are written to Redis every minute. Before publishing, a safety filter removes spam, abusive terms and coordinated manipulation; this step is a real requirement, not an afterthought.

IDs, ordering and pagination

Tweet IDs are 64-bit Snowflake IDs: 41 bits of milliseconds since a custom epoch, 10 bits of worker ID and 12 bits of sequence, so each worker can make 4,096 IDs per ms without coordination. Because IDs are roughly time-ordered, every list (home timeline, user timeline, search results) can be sorted and paged by ID. Pagination uses max_id/since_id cursors: 'give me tweets with ID < X'. This is stable when new tweets arrive at the top, unlike offset pagination, which would show duplicates. Clock skew between workers can produce slightly out-of-order IDs; this is acceptable for a timeline. Workers must never move their clock backwards: if NTP steps time back, the generator waits or refuses until time catches up.

8 Trade-offs

Push vs pull for timelines

Push makes reads cheap at the cost of write amplification and storage; pull makes writes cheap and reads expensive. Twitter's read:write ratio favours push with a pull exception for big accounts. State the threshold and that it is tuned from metrics.

Chronological vs ranked timeline

Chronological ('Following') is predictable and easy to page. Ranked ('For you') adds out-of-network recommendations and engagement models, at the cost of a candidate-generation and ranking layer. Offer both; the storage layer above serves both.

Exact vs approximate counts

Like and retweet counts and trend counts do not need to be exact in real time. Approximate, batched counters (or sketches) cut write load massively. Exact counts can be reconciled in batch.

9 Bottlenecks

Event spikes

During a World Cup final, tweets/s can jump 10x within seconds. Kafka absorbs the burst; fan-out lag grows briefly. Autoscale fan-out workers on consumer lag, and pre-scale before known events (IPL final, elections).

Hot tweet reads

A viral tweet is read millions of times per minute. Cache it in Memcached with replicas and a small in-process cache in Timeline Service so one cache shard does not melt.

Search fan-out

Every query hits every partition of the recent tier. As partitions grow, add replicas for throughput and keep the hot tier small through time tiering.

10 Failure handling

Redis timeline node lost

Timelines are a cache with replicas; if all copies are lost, the Timeline Service rebuilds by pull (merge recent tweets from followed users) and repopulates. Slower first load, no data loss.

Fan-out backlog

If workers stall, tweets still appear in user timelines and search; home timelines are stale. Kafka keeps the events; workers catch up. Celebrity pull path is unaffected.

Search cluster partition down

Queries return partial results from healthy partitions with a flag rather than failing. Replicas take over; a rebuilt partition replays from Kafka.

11 What impresses

  • Quoting the fan-out maths and why the threshold exists
  • Explaining trends as spike detection against a baseline, not raw counts
  • Search freshness via an append-optimised in-memory index fed by Kafka
  • Using time-sortable IDs for all pagination
  • Handling Indian multilingual and Hinglish text in search tokenisation
  • Rebuild-by-pull as the recovery path for the timeline cache

12 Mistakes

  • SELECT ... WHERE user_id IN (followees) ORDER BY time on every timeline load
  • Fanning out to all 100M followers of a celebrity
  • Using offset pagination on a live timeline
  • Treating trends as the most frequent hashtags of all time
  • Building search with SQL LIKE queries
  • Spending all the time on posting and none on reading

13 Follow-ups

Q1How would you build the 'For you' ranked timeline with out-of-network tweets?
Q2How do you handle deleting a tweet that is already in millions of timelines?
Q3How would you implement the notifications tab (mentions, likes)?
Q4How do you stop bots from manipulating trends?
Q5How would you shard the follow graph, and how do you count followers?
Q6How does the design change for a multi-region deployment?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free