Design Twitter: A Step-by-Step System Design Walkthrough
Design Twitter step by step: use cases, entities and constraints, APIs, an incremental high-level design, and deep dives on celebrities, trends and search.
"Design Twitter" is one of the most common system design interview questions, and a good one to learn the interview method on. This walkthrough solves it the way you would in a 45–60 minute round: turn the vague prompt into use cases, entities and constraints with the problem decomposition framework, define the APIs, build the architecture one justified component at a time, then go deep on the celebrity problem, trends and search.
Try it yourself first. Design Twitter on a blank canvas, then read on and compare. The news feed practice round is the same problem with an AI interviewer.
Step 0: use cases from the verbs
What can users do on Twitter? They post tweets, view their feed, follow other users, like tweets and comment on tweets. Each verb becomes a use case:
| Verb | Use case |
|---|---|
| Post | Create a new tweet |
| View | Read a single tweet or a user's feed |
| Follow | Create a relationship between two users |
| Like | Increment an engagement counter on a tweet |
| Comment | Create a reply attached to a tweet |
Then define what correct means for each. If the same text is posted twice, is that two tweets or a duplicate? Interviews assume no deduplication unless told otherwise. Must followers see new tweets immediately? That depends on the constraints in step 2. Must deleted tweets be recoverable? Not for this problem. With 45–60 minutes, these five core operations are plenty; resist adding direct messages, notifications or ads.
Step 1: entities from the nouns
"Users post tweets" gives User and Tweet. "Users follow other users" gives a Follow relationship. Likes and comments are both ways of engaging with a tweet, so they become one Engagement entity.
| Entity | What it holds | Owner (source of truth) |
|---|---|---|
| User | An account and its profile | User service → user database |
| Tweet | Content, timestamp and author | Tweet service → tweet database |
| Follow | Follower → followee relationship | Follow service → follow database |
| Engagement | Likes and comments on tweets | Engagement service → engagement database |
Clear ownership prevents conflicting writes. If two services could both update a tweet, which write wins? Making the tweet service the single source of truth for tweets removes that question.
Step 2: constraints from the adjectives
The prompt says Twitter must be low latency, highly available, scalable and durable. Each adjective forces specific components:
| Adjective | What it means here | What it forces |
|---|---|---|
| Low latency | Feeds load in under 500 ms | A cache: precompute feeds in Redis and read from it instead of querying the database on every request |
| Highly available | A crashed service must not lose a tweet that was already accepted | A message queue such as Kafka to buffer writes; messages survive service outages and are processed on recovery |
| Scalable | Around 400 million monthly active users | Horizontal scaling behind load balancers, and database partitioning |
| Durable | Tweets must never be lost | A distributed database with replication across nodes, plus backups |
Every component in the final design traces back to one of these. Without the latency requirement there is no reason for a cache; without availability, no reason for a queue.
Design checkpoint. The interviewer says the feed must be highly available but never mentions latency. What do you add first? Not a cache "because caches always help". Availability is about surviving partial failure, so the justified additions are a message queue (accepted writes are not lost when a service dies) and replication (data survives a node failure).
API design
This step takes a few minutes, and many candidates overcomplicate it. Write one endpoint per functional requirement. Interviewers look for readable paths (/tweet, not /item), clear request and response data, and the right HTTP methods (POST to create, GET to fetch). The author always comes from the authenticated session, never from the request body. See API design.
Post a tweet
POST /tweet
{ "content": "Shipping the new search today", "media_ids": [] }
201 Created
{ "tweet_id": "1834455501", "created_at": "2026-10-20T09:15:02Z" }View a tweet
GET /tweet/{id}
200 OK
{ "tweet_id": "1834455501", "author": { "id": "42", "handle": "ana" },
"content": "Shipping the new search today", "created_at": "...",
"like_count": 120, "comment_count": 8 }View the feed
GET /feed?cursor=<opaque>&limit=20
200 OK
{ "tweets": [ { "tweet_id": "...", "author": {...}, "content": "..." } ],
"next_cursor": "eyJ0cyI6..." }Feeds use cursor pagination, because new tweets keep arriving at the top and offsets would skip or repeat items.
Follow a user
POST /follow
{ "followee_id": "77" }
200 OK (idempotent: following twice is not an error)Like and comment
POST /tweet/like
{ "tweet_id": "1834455501" }
200 OK
POST /tweet/comment
{ "tweet_id": "1834455501", "content": "Congrats!" }
201 Created
{ "comment_id": "9001" }High-level design
This is the bulk of the interview, usually 15–20 minutes; juniors spend longer here, seniors move to deep dives sooner. Start with the simplest data flow that supports the use cases, so the interviewer can correct mistakes early. Then add components one at a time, each justified by a constraint from step 2. In the diagrams below, the highlighted component is the one added at that step.
Basic data flow
Map each use case to a service with its own database. (For when to use services at all, see monolith vs microservices.)
That is a lot of services. Separation of concerns matters, but don't turn everything into a microservice. Likes and comments are logically similar: both are engagement on a tweet, both are essentially counters with some data attached. Merge them into one engagement service with one engagement database.
With several services, clients shouldn't need to know each one's address. Add an API gateway as the single entry point; it also handles authentication, rate limiting and routing.
Applying the constraints
This design works, but it falls over at scale. Now apply the four constraints. The biggest mistake candidates make here is adding components without saying which requirement each one serves.
Scalability first, since it's the most straightforward. Run multiple instances of every service (tweet, feed, follow, engagement); they handle requests in parallel and independently. Put a load balancer in front to spread traffic evenly. As a bonus, the load balancer health-checks each instance and stops sending traffic to unhealthy ones, which also helps availability. Scale each service on its own traffic: add feed service instances at peak hours and remove them when it's quiet. See autoscaling.
Low latency next. With a few users everything is fast: each feed request reads a handful of tweets. With millions of users and billions of tweets, hitting the database for every feed request gets very slow, and it's also wasteful: tweets are written to the database and then re-read for every feed load. A cache fixes this.
A cache is fast in-memory storage close to the application. The feed service uses a distributed cache such as Redis or Memcached to hold each user's recent feed. When someone posts, the new tweet isn't only written to the database: it is also pushed into the cached, precomputed feeds of their followers. When a follower opens the app, the feed service reads straight from the cache, with no database query and no on-the-fly aggregation.
Caches are ideal for hot data like the latest tweets, and because they work in memory they answer in milliseconds. Offloading repeated reads also takes load off the database, leaving it free for writes. Keep the cache fresh with expiry times (TTLs) or, better here, with event-driven updates: each new tweet triggers an update to the affected feeds. See cache strategies and cache invalidation.
High availability. Health checks help, but consider this: a user tweets at 1:02 pm, the tweet service crashes at 1:03, and followers open the app at 1:04. Do they see the tweet? With direct service-to-service calls, no: the tweet was accepted but never reached the feed. And if millions of users tweet at once, processing everything synchronously overloads the servers. A message queue solves both.
The queue sits between services, decoupling them and making sure accepted messages (new tweets) are not lost even if a service is down. The tweet service no longer calls the feed service. It puts the tweet on the queue; consumers write it to the database and to the cache; the feed service reads the cache. If the tweet service goes offline right after accepting a tweet, the message is already safe in the queue. Followers still see it.
Queues give eventual consistency and high availability through partial failures, and they help scalability too, absorbing traffic spikes without overwhelming downstream services. RabbitMQ, Kafka and AWS SQS are all built for this kind of durable, reliable delivery. See message queues.
Durability last. With billions of tweets, likes and follows, lost data can't be recovered. Use distributed databases that replicate data across nodes.
Databases such as Amazon DynamoDB, Google Cloud Spanner and Cassandra replicate automatically across nodes, so data stays available when a node fails. They also offer point-in-time recovery and automated backups. Take regular snapshots of the tweet, follow and engagement databases and keep them in separate backup storage, so even a total failure can be restored to the last consistent state. See replication and disaster recovery.
Deep dives
Deep dives close the interview. They test advanced features, domain-specific edge cases and trade-offs beyond the high-level architecture. Three classic ones for Twitter:
How would you handle the celebrity problem?
When an account with millions of followers tweets, fan-out on write has to insert that tweet into millions of cached feeds. That is a huge burst of writes (write amplification) and it delays everyone else's updates.
| Author's followers | Pure push: timeline writes per post | Hybrid (threshold 10K): writes per post |
|---|---|---|
| 300 | 300 | 300 (push) |
| 6,000 | 6,000 | 6,000 (push) |
| 2,000,000 | 2,000,000 | 0 (pull at read time) |
| 100,000,000 | 100,000,000 | 0 (pull at read time) |
Keep fan-out on write for normal users: their tweets are pushed into followers' feeds as soon as they post, so reads stay cheap. For accounts above a threshold (say 10,000 followers), switch to fan-out on read: their tweets are stored in the tweet database and tweet cache but not copied into follower feeds. When a follower opens their feed, the feed service fetches the latest tweets of the celebrities they follow and merges them in. Decide per account using follower count or engagement volume. See fan-out for the full push vs pull comparison.
| Answer quality | Approach | Why |
|---|---|---|
| Weak | Pure fan-out on write for everyone | One celebrity post triggers millions of writes and delays all other fan-out. |
| Good | Hybrid push/pull with a follower threshold | Normal users keep cheap reads; celebrity posts cost no fan-out writes. |
| Great | Hybrid, plus a hot cache for celebrity timelines | The pulled celebrity tweets are read by millions, so keep them in a dedicated, replicated cache to avoid a hot key; merge them into feeds at read time. |
How would you support trends and hashtags efficiently?
Trends mean aggregating billions of tweets in near real time to find popular topics. Compute them in two levels: each region or data center counts hashtags and keywords over a sliding window (say the last 15 minutes) and sends its local results to a global aggregation service, which combines them into global trends. See windowing.
Have the tweet service index hashtags when a tweet is created, using an inverted index where each hashtag maps to the IDs of tweets containing it, stored in a distributed search engine such as Elasticsearch or Solr. Recompute trends periodically (every minute) and cache them in Redis with a TTL, so trend reads are instant and refreshes don't overload the system. Approximate counters such as a count-min sketch keep the counting cheap at this volume.
How would you handle tweet search at scale?
Search across tweets, hashtags and profiles needs its own architecture:
- Index asynchronously. The tweet service publishes new tweets to the queue; an indexing service consumes them and updates a distributed search engine such as Elasticsearch or Apache Solr.
- Partition the index by time (one index per day) or by hashtag to spread load. Keep recent indexes on fast nodes and move old ones to cheaper storage.
- Use an inverted index for keyword and hashtag lookups, and rank results with BM25 or an ML model using engagement, recency and other signals. See search and inverted indexes.
- Cache popular queries and their results to take load off the search cluster.
What to remember
- Verbs → use cases, nouns → entities with one owner each, adjectives → constraints that justify components.
- Writes go through a queue; feeds are precomputed into a cache; the database is replicated for durability.
- Fan-out on write for normal users, fan-out on read for celebrities.
- Trends come from windowed counts aggregated regionally then globally; search uses a separate, time-partitioned inverted index fed from the queue.
Where it’s used
Instagram, LinkedIn and Facebook feeds use the same precomputed-timeline-plus-hybrid-fan-out design.
GitHub's activity feed and Slack's unread counts are fan-out problems with the same push vs pull trade-off.
Per-user inboxes in a cache, filled asynchronously from a queue, are the same pattern.
In an interview
- Spend the first minutes on verbs, nouns and adjectives; it gives the rest of the interview a clear structure.
- Build the design incrementally and name the constraint each new component satisfies.
- Volunteer the celebrity problem before you're asked; it's the deep dive interviewers expect.
- State the consistency trade-off: followers see a new tweet eventually (seconds), not instantly.