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.

Intermediate 9 min read · Updated

"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:

VerbUse case
PostCreate a new tweet
ViewRead a single tweet or a user's feed
FollowCreate a relationship between two users
LikeIncrement an engagement counter on a tweet
CommentCreate 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.

EntityWhat it holdsOwner (source of truth)
UserAn account and its profileUser service → user database
TweetContent, timestamp and authorTweet service → tweet database
FollowFollower → followee relationshipFollow service → follow database
EngagementLikes and comments on tweetsEngagement 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:

AdjectiveWhat it means hereWhat it forces
Low latencyFeeds load in under 500 msA cache: precompute feeds in Redis and read from it instead of querying the database on every request
Highly availableA crashed service must not lose a tweet that was already acceptedA message queue such as Kafka to buffer writes; messages survive service outages and are processed on recovery
ScalableAround 400 million monthly active usersHorizontal scaling behind load balancers, and database partitioning
DurableTweets must never be lostA 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.)

reads tweetsClientTweet serviceFollow serviceFeed serviceLike serviceComment serviceTweet DBFollow DBLike DBComment DB
One service per use case, each in front of its own database.

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.

ClientTweet serviceFollow serviceFeed serviceEngagement serviceTweet DBFollow DBEngagement DB
Likes and comments merged into a single engagement service.

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.

ClientAPI gatewayTweet serviceFollow serviceFeed serviceEngagement service
An API gateway becomes the single entry point and routes each request to the right service.

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.

API gatewayLoad balancerTweet service ×NFollow service ×NFeed service ×NEngagement service ×N
Each service runs as N identical instances behind a load balancer that also health-checks them.

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.

read feedon missFeed serviceFeed cacheRedisTweet DB
The feed service reads precomputed feeds from a cache instead of assembling them from the database on every request.

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.

Tweet serviceMessage queueConsumerFeed cacheTweet DBFeed service
The tweet service publishes to a queue; consumers write the tweet database and the followers' cached feeds, which the feed service reads.

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.

replicateConsumerTweet DB primaryReplicaReplicaBackupspoint-in-time snapshots
Each database is replicated across nodes, so losing one node loses no data; snapshots go to separate backup storage.

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.

fan outwho follows?ClientAPI gateway+ load balancingTweet serviceFollow serviceFeed serviceEngagement serviceQueueConsumersFeed cacheTweet DBFollow DBEngagement DB
The complete high-level design.

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 followersPure push: timeline writes per postHybrid (threshold 10K): writes per post
300300300 (push)
6,0006,0006,000 (push)
2,000,0002,000,0000 (pull at read time)
100,000,000100,000,0000 (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 qualityApproachWhy
WeakPure fan-out on write for everyoneOne celebrity post triggers millions of writes and delays all other fan-out.
GoodHybrid push/pull with a follower thresholdNormal users keep cheap reads; celebrity posts cost no fan-out writes.
GreatHybrid, plus a hot cache for celebrity timelinesThe 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

Any social feed

Instagram, LinkedIn and Facebook feeds use the same precomputed-timeline-plus-hybrid-fan-out design.

Activity streams

GitHub's activity feed and Slack's unread counts are fan-out problems with the same push vs pull trade-off.

Notification inboxes

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.