Design Twitter

Design Twitter step by step: requirements, scale, data model, hybrid fan-out, reliability, and the reasoning behind every major decision.

Page content

Twitter is a news-feed system.

Users publish short posts, follow other users, and open Home to see a combined stream of recent tweets. The main design question is:

How do we generate that home timeline quickly when some authors have hundreds of followers and others have tens of millions?

We will answer that question gradually. First we will define the product, estimate the traffic, and model the basic data. Then we will compare ways to generate the feed and let the workload lead us to the final architecture.

1. Clarify the problem

“Design Twitter” is too broad for one interview. I would begin with a few questions:

  • Are we designing the chronological Following feed or the ranked For You feed?
  • Are tweets text-only, or is media part of the core design?
  • Do we need likes, replies, retweets, search, notifications, and trends?
  • Are protected accounts and blocks in scope?
  • Must new tweets appear live, or can users refresh?
  • What traffic and retention should we support?

If the interviewer gives no additional constraints, I would state these assumptions:

Feed             Reverse-chronological Following feed
Tweet content    Text in the core design; media later
Users            Logged in
Visibility       Public and protected accounts
Delivery         Refresh-based; a few seconds of feed lag is acceptable
Retention        Keep tweet metadata until the author deletes it

This keeps the discussion focused on feed generation. Ranking, media, and search can be added after the core path works.

2. Functional requirements

The system must allow a user to:

  1. Publish and delete a tweet.
  2. Follow and unfollow another user.
  3. Read a home timeline containing tweets from followed accounts.
  4. Read a profile timeline containing tweets from one author.
  5. Fetch a single tweet by id.
  6. See only content they are authorized to view.

The initial design does not include:

  • direct messages;
  • ads and recommendations;
  • full-text search;
  • likes and retweets;
  • notifications and trends; or
  • video processing.

We will briefly explain how these fit at the end.

3. Non-functional requirements

The important quality targets are:

RequirementTarget
Home latencyp99 below 200 ms
Post latencyp99 below 300 ms
Read availabilityApproximately 99.99%
DurabilityNever lose an acknowledged tweet
Author consistencyThe author sees their own tweet immediately
Follower consistencyA delay of a few seconds is acceptable
PrivacyNever expose blocked or protected content incorrectly
ScalabilityHandle skew from accounts with millions of followers

Consistency is not one global setting.

For a new tweet:

Author's profile       immediate
Follower's Home        may lag by a few seconds
Tweet durability       must be immediate
Privacy changes        must be enforced before content is returned
Like/follower counters may lag

This distinction will allow us to move expensive feed delivery out of the synchronous post request without weakening durability or privacy.

Edge cases to keep in mind

  • A celebrity publishes to 30 million followers.
  • A mobile client retries after timing out.
  • Alice follows Bob and immediately opens Home.
  • Alice unfollows Bob while fan-out is still running.
  • Bob deletes a tweet already present in millions of timelines.
  • Fan-out workers receive duplicate or out-of-order events.
  • Redis or Kafka becomes unavailable.
  • Breaking news creates a sudden read spike.

4. Estimate the scale

Use round numbers. We need enough arithmetic to find the bottleneck.

Assume:

Daily active users                    250 million
Home opens per user per day           5
New tweets per day                    150 million
Average accounts followed             200
Peak multiplier                       5×
Timeline page size                    20 tweets

Home timeline traffic

250M users × 5 opens
= 1.25 billion Home requests/day

Average QPS
= 1.25B / 86,400
≈ 14,500 QPS

Peak QPS
≈ 14,500 × 5
≈ 72,500 QPS

Tweet creation traffic

150M tweets / 86,400
≈ 1,700 writes/second average

Peak
≈ 8,500 writes/second

The first useful conclusion is:

Feed reads greatly outnumber tweet writes.

That suggests doing more work when a tweet is created if it can make every later read cheaper.

Storage

If tweet metadata is roughly 300 bytes:

150M × 300 bytes
≈ 45 GB/day
≈ 16 TB/year before replication and indexes

This is manageable with partitioned storage. Media is far larger, so it will live in object storage.

5. What are we generating?

Suppose Alice follows Bob, Cara, and Dan.

Their recent tweets are:

Bob   → B1, B2
Cara  → C1
Dan   → D1, D2

Ordered newest first, Alice’s Home might be:

B2
D2
C1
B1
D1

So the core query is:

Given Alice, return the newest tweets written by accounts Alice follows.

Everything else in the architecture exists to answer that query quickly and correctly.

6. APIs

Keep the API small and independent of the storage design.

Create a tweet

POST /v1/tweets
Authorization: Bearer <token>
Idempotency-Key: <uuid>

{
  "text": "hello world",
  "media_ids": []
}
HTTP 201 Created

{
  "tweet_id": "t_9f3a",
  "created_at": "2026-08-14T06:01:00Z"
}

The idempotency key is important. If the client times out after the server creates the tweet, retrying the same request should return the original tweet rather than create a duplicate.

Read Home

GET /v1/timelines/home?cursor=<opaque>&limit=20
HTTP 200 OK

{
  "tweets": [
    {
      "tweet_id": "t_9f3a",
      "author": {
        "user_id": "u_bob",
        "handle": "bob"
      },
      "text": "hello world",
      "created_at": "2026-08-14T06:01:00Z"
    }
  ],
  "next_cursor": "eyJ0cyI6MTcyMzYxNTI2MCwiaWQiOiJ0XzlmM2EifQ"
}

Other core endpoints:

DELETE /v1/tweets/{tweet_id}
GET    /v1/tweets/{tweet_id}
GET    /v1/users/{user_id}/tweets?cursor=...&limit=20

POST   /v1/users/{user_id}/follow
DELETE /v1/users/{user_id}/follow
GET    /v1/users/{user_id}/followers?cursor=...
GET    /v1/users/{user_id}/following?cursor=...

7. Basic data model

We need three logical datasets.

User

User
----
user_id
handle
display_name
is_protected
created_at

Tweet

Tweet
-----
tweet_id
author_id
text
media_ids
created_at
deleted_at

The main lookups are:

tweet_id  → one tweet
author_id → recent tweets by that author

Follow

Follow
------
follower_id
followee_id
created_at

If Alice follows Bob:

Alice → Bob

We need both directions:

Who does Alice follow? → used when Alice reads Home
Who follows Bob?       → used when Bob publishes

These are direct one-hop lookups, so a graph database is not necessary.

Tweets and follow relationships are facts we cannot lose. A generated home timeline is different: it is a shortcut that can be rebuilt from those facts.

Source of truth       Tweets, follows, privacy settings
Derived data          Profile timelines, home timelines, counters
Cache                 Hot tweet objects and timelines
Event system          Carries changes to background consumers

We will choose actual databases after understanding how the feed is generated.

8. The central decision: how should we generate Home?

There are two basic approaches:

Fan-out on read     Build the feed when Alice opens Home
Fan-out on write    Prepare Alice's feed when followed users publish

Neither is always better. We need to compare their cost against our workload.

9. Approach 1: fan-out on read

When Alice opens Home:

Alice
Load accounts Alice follows
Load recent tweets from each account
Merge by creation time
Return the newest 20

If Alice follows 200 accounts, the service may need to read and merge 200 small timelines on every refresh.

Advantages

  • Creating a tweet is cheap.
  • Celebrity posts cost no more than ordinary posts.
  • We do not store a separate home timeline for every user.

Disadvantages

  • Read cost grows with following count.
  • The same merge is repeated on every refresh.
  • It is difficult to keep p99 below 200 ms at 72,500 peak QPS.

This is a good design for an early product. It is also a useful fallback when precomputed feeds are unavailable. At Twitter scale, it makes the dominant read path too expensive.

10. Approach 2: fan-out on write

Instead of waiting for Alice to read, prepare her timeline when Bob posts.

Bob creates tweet B3
Find Bob's followers
Add B3 to each follower's timeline
Alice's Home already contains B3

Alice’s feed can now be stored as an ordered list of tweet ids:

home:alice

[B3, D2, C1, B2, D1, ...]

Reading Home becomes:

Alice
Read top 20 tweet ids
Fetch 20 tweet objects
Return

This is much cheaper than merging hundreds of author timelines for every read.

Advantages

  • Home reads are fast and predictable.
  • We perform work once per follower instead of once per refresh.
  • Cursor pagination is straightforward.

Disadvantages

  • Every tweet creates many timeline writes.
  • Inactive users receive updates they may never read.
  • Authors with millions of followers create enormous fan-out jobs.

11. The celebrity problem

With an average of 200 followers:

150M tweets × 200 followers
= 30 billion timeline writes/day
≈ 347,000 writes/second average

This can be distributed across many workers.

Now suppose a celebrity has 30 million followers:

1 tweet
30 million timeline writes

Even at 100,000 writes per second, that one fan-out takes about five minutes.

We should not make the author wait, and we should not let one celebrity tweet delay every ordinary tweet behind it.

Pure fan-out on write solves the common case but behaves badly for the highest-degree users.

12. The practical answer: hybrid fan-out

Use both models.

Ordinary author     → fan-out on write
Celebrity author    → fan-out on read

For example:

Bob has 800 followers
  → push Bob's tweet into follower timelines

Celeb has 30M followers
  → store the tweet only in Celeb's profile timeline

When Alice opens Home:

Alice's precomputed timeline
        +
Recent tweets from celebrities Alice follows
Merge by time
Return top 20

Most users follow only a few very large accounts, so this merge is far cheaper than merging every followed account.

We might start with a threshold such as 5,000 followers:

followers < 5,000    push
followers ≥ 5,000    pull

The threshold should not be a permanent hardcoded product rule. Tune it using:

  • active follower count;
  • fan-out completion time;
  • worker capacity;
  • feed-read frequency; and
  • storage cost.

The hybrid model is more complex, but it handles both the common case and the celebrity case efficiently.

13. High-level architecture

Now we have enough information to draw the main components.

                           ┌──────────────┐
                           │   Clients    │
                           └──────┬───────┘
                           ┌──────▼───────┐
                           │ API Gateway  │
                           │ Auth + limits│
                           └───┬────┬─────┘
                               │    │
                 ┌─────────────┘    └──────────────┐
                 ▼                                 ▼
          ┌─────────────┐                   ┌──────────────┐
          │Tweet Service│                   │Timeline Svc. │
          └──────┬──────┘                   └───┬──────┬───┘
                 │                              │      │
          ┌──────▼──────┐                  ┌────▼───┐  │
          │ Tweet Store │                  │ Feed   │  │
          │ + Outbox    │                  │ Store  │  │
          └──────┬──────┘                  └────────┘  │
                 │                                      │
                 ▼                                      ▼
              Kafka                              Tweet Cache/DB
          Fan-out Workers ◄──────── Graph Service ─────► Follow DB
             Feed Store

Why each component exists

  • Tweet Service: validates and durably stores tweets.
  • Graph Service: answers following and follower lookups.
  • Timeline Service: assembles a page of Home.
  • Feed Store: holds precomputed tweet ids for active users.
  • Kafka: separates tweet creation from asynchronous fan-out.
  • Fan-out workers: perform the high-volume timeline writes.
  • Tweet cache: prevents every timeline read from hitting the Tweet Store.

Posting flow

  1. The client sends a tweet to the Tweet Service.
  2. The service stores it durably.
  3. It publishes an event through the outbox.
  4. The API returns without waiting for fan-out.
  5. Workers consume the event, load followers, and update feed entries.

Home flow

  1. The Timeline Service reads precomputed tweet ids.
  2. It also reads recent tweets from celebrity followees.
  3. It merges and paginates the candidates.
  4. It fetches complete tweet objects.
  5. It applies current visibility rules.
  6. It returns the page.

14. Posting a tweet

The write path should be:

Authenticate and rate-limit
Validate text and idempotency key
Generate tweet_id
Store tweet durably
Append to author's profile timeline
Warm tweet cache
Create fan-out event
Return 201

The tweet record is the source of truth. The author’s profile should show it immediately.

Follower delivery is asynchronous. Post latency therefore does not depend on whether the author has 50 followers or 50 million.

15. Why Kafka?

Without a queue:

POST /tweets
Store tweet
Find followers
Write thousands of timelines
Return response

Post latency would grow with follower count, and a traffic spike could exhaust request threads.

With Kafka:

POST /tweets
Store tweet
Publish event
Return

Kafka → workers → feed entries

Kafka solves four problems:

  1. It decouples post latency from fan-out work.
  2. It absorbs short bursts.
  3. It allows fan-out, search, and notifications to consume the same event independently.
  4. It retains events so consumers can retry or rebuild derived data.

A managed queue such as SQS or Pub/Sub is a valid alternative if we mainly need task delivery. Kafka is attractive when replay, multiple consumers, measurable lag, and partition ordering matter. Its trade-off is operational complexity.

Avoid the dual-write problem

This sequence is unsafe:

Database write succeeds
Kafka publish fails

The tweet would exist but never reach followers.

Use a transactional outbox:

One database transaction
    ├── insert tweet
    └── insert outbox event

Outbox relay
    └── publish event to Kafka

If Kafka is unavailable, the outbox record remains and the relay retries later. The tweet is delayed, not lost.

16. Fan-out workers

A worker should not load millions of followers into memory.

Instead:

TweetCreated
Read follower count
Ordinary author?
    ├── no  → stop; tweet will be pulled on read
    └── yes → create follower-page jobs
              500 followers/job
              write feed entries

Small jobs can run in parallel and retry independently.

Partition tweet events by author_id. This preserves ordering for one author’s tweets while allowing different authors to process concurrently. We do not need one global order across all tweets.

Kafka commonly delivers at least once, so duplicate events are expected. Make the feed write idempotent by using tweet_id as the unique member. Adding the same tweet twice has the same result as adding it once.

If consumers fall behind:

  • scale workers based on consumer lag;
  • cap concurrency for unusually large authors;
  • prioritize recent events;
  • retain events until consumers recover; and
  • temporarily pull recent profile tweets during Home reads.

That last fallback helps users see fresh tweets while fan-out catches up.

17. Reading the feed

The Feed Store contains ids, not complete tweets:

home:alice

[t_100, t_98, t_92, t_87]

The read path is:

Read candidate tweet ids
Add celebrity candidates
Merge by (created_at, tweet_id)
Fetch tweet objects in one batch
Filter deleted and unauthorized tweets
Return 20 results

Fetching full objects from ids is often called hydration.

Why not copy full tweet JSON into every feed?

Suppose Bob deletes a tweet copied into ten million timelines. If those timelines contain the full object, ten million copies become stale. If they contain only an id, we delete one canonical Tweet record, invalidate one cache entry, and let hydration skip stale ids.

If filtering removes five candidates, the Timeline Service fetches more ids until it fills the page or reaches the end.

18. Follow and unfollow

Follow

Suppose Alice follows Bob.

We can choose:

Option A   Show only tweets Bob creates from now on
Option B   Backfill Bob's recent tweets into Alice's feed

Most products choose some form of backfill:

Alice follows Bob
Store follow relationship
Fetch Bob's latest N tweet ids
Add them to Alice's feed

Backfill is asynchronous. Until it completes, the read path can also pull Bob’s recent profile tweets so Home does not appear broken immediately after Follow.

Unfollow

When Alice unfollows Bob, do not scan and rewrite her entire timeline synchronously.

Remove follow relationship
Filter Bob's stale candidates during reads
Clean old feed entries asynchronously

The follow record is authoritative. The feed is only derived data.

Blocks and protected accounts use the same principle: verify current permission before returning the tweet.

19. Pagination

Do not use offset pagination for a changing feed.

With:

GET /home?offset=20

new tweets inserted above the current page move every offset. The user can see duplicates or skip entries.

Use a cursor containing the last item’s ordering fields:

cursor = (created_at, tweet_id)

The next request asks for entries older than that pair:

GET /v1/timelines/home?cursor=<encoded-pair>&limit=20

tweet_id breaks ties when two tweets have the same timestamp.

For a ranked feed, the cursor may instead contain a stable ranking score, tweet id, and feed/session version.

20. Storage choices

Choose storage from the access pattern.

Tweet Store

We need:

tweet_id  → one tweet
author_id → recent tweets, newest first

A sharded relational database is a good starting point because it supports transactions and the outbox cleanly.

At larger scale, Cassandra or DynamoDB-style tables are also a strong fit:

tweets_by_id
Partition key: hash(tweet_id)

tweets_by_author
Partition key: (author_id, time_bucket)
Sort key:      (created_at, tweet_id)

The time bucket prevents one author’s partition from growing forever.

The reason to choose a distributed KV or wide-column store is not simply “NoSQL scales.” It is that our reads are predictable key and ordered-range lookups, and we need horizontal write throughput. The trade-off is more denormalization and less transactional flexibility.

Follow Store

We need both adjacency directions:

following_by_user
Partition key: follower_id

followers_by_author
Partition key: (followee_id, bucket)

Bucket the follower side because one celebrity may have tens of millions of edges.

A relational store works early. A wide-column or KV store fits the large-scale access pattern. A graph database is unnecessary because we are not traversing several relationship hops.

Feed Store

We need:

user_id → recent ordered tweet ids

Redis sorted sets are useful for the hot portion:

Key       home:{user_id}
Score     timestamp or rank score
Member    tweet_id

Keep perhaps the latest 500–800 ids and expire inactive users.

If losing Redis cannot be tolerated, also store timelines in Cassandra, DynamoDB, or another durable append-friendly store. If pull-based rebuilding is acceptable, Redis can remain an ephemeral serving layer. That is a recovery-time trade-off, not a universal rule.

21. Caching

Useful caches include:

CacheKeyWhy
Tweettweet:{tweet_id}Avoid fetching the same popular tweet repeatedly
Useruser:{user_id}Author details appear on every hydrated tweet
Followingfollowing:{user_id}Avoid repeated graph reads
Homehome:{user_id}Serve recent feed ids quickly
Profileprofile:{user_id}Celebrity timelines are read frequently

TTL and invalidation

Tweet deleted        invalidate tweet:{id}
Profile updated      invalidate user:{id}
Follow changed       invalidate following:{user_id}
Home timeline        append/trim; expire inactive users

On a miss, read the source store and refill the cache.

Hot keys and stampedes

A viral tweet may receive millions of reads. Warm its cache during creation, replicate very hot entries if one node becomes network-bound, and coalesce simultaneous misses so only one request reaches the database.

Use TTL jitter to prevent many popular keys expiring at the same instant.

Redis accelerates reads. It is not the source of truth.

22. Partitioning and horizontal scaling

Partition each dataset using the field used to read it:

DatasetPartition keyReason
Tweet by idhash(tweet_id)Spread current writes evenly
Author timeline(author_id, time_bucket)Read one author’s ordered tweets
Followingfollower_idRead one user’s followees together
Followers(followee_id, bucket)Spread celebrity follower lists
Home timelineuser_idRead one user’s feed together
Kafka eventsauthor_idPreserve per-author ordering

Stateless API services scale behind load balancers. Stateful stores scale by adding partitions.

Celebrity accounts still create hot keys even after general sharding. Give them additional follower buckets, cache replicas for profile reads, and isolated worker capacity so they cannot dominate ordinary traffic.

23. Consistency and correctness

Tweet creation

The Tweet record must be durably committed before the API returns. Fan-out may happen later.

Author’s profile

The author should see the tweet immediately. Update the profile timeline in the write path, or read recent self-authored tweets directly from the Tweet Store while the projection catches up.

Follower timelines

Eventual consistency is acceptable. The outbox guarantees that a committed tweet eventually produces an event.

Follow and unfollow

The Follow record changes before success is returned. Cached and precomputed timelines may lag, so read-time filtering uses current graph state.

Protected accounts and blocks

Never rely on feed membership as authorization. If permission cannot be verified, fail closed for protected content.

Deletes and out-of-order events

Publish a version or event timestamp with create and delete events. Keep a deletion tombstone so a delayed TweetCreated event cannot resurrect a deleted tweet in a derived store.

Counters

Follower and like counts can be eventually consistent. Reconcile them periodically from source records. Never use an approximate counter as the only authorization check.

24. Failure scenarios

Kafka is unavailable

The Tweet Service still commits the tweet and outbox record. The relay retries when Kafka recovers. Followers see a delay, but no tweet is lost.

Alert on:

oldest unpublished outbox record
outbox backlog size
Kafka publish failure rate

Feed Store or Redis is unavailable

Possible degraded behavior:

Durable feed store available → serve from it
No durable feed store        → bounded fan-out on read
Database under pressure      → return a stale cached page

Use admission control so fallback traffic does not turn a Redis outage into a Tweet Store outage.

Fan-out workers fall behind

Kafka retains events. Scale workers based on lag, not CPU alone. Home can merge recent author timelines while the backlog drains.

A dependency becomes slow

Use:

  • short, explicit timeouts;
  • bounded retries with exponential backoff and jitter;
  • circuit breakers;
  • separate connection pools for critical and optional work; and
  • partial responses when safe.

Unlimited retries create a retry storm and spread one dependency’s failure through the system.

Traffic suddenly spikes

  • Serve cached tweets and profiles.
  • Autoscale stateless services and consumers.
  • Rate-limit tweet creation and excessive refreshes.
  • Delay nonessential counters and analytics.
  • Isolate celebrity workloads.

Duplicate delivery

At-least-once delivery means duplicates are normal. Feed writes, deletes, and notification aggregation must be idempotent.

25. Security and abuse prevention

Authentication

The API Gateway validates a session or OAuth token. Internal services authenticate using workload identities or mTLS.

Authorization

The Tweet Service verifies ownership before deletion. The Timeline Service checks protected-account, block, and visibility rules before returning content.

Rate limiting

Use token buckets by user and IP for:

  • tweet creation;
  • follow/unfollow actions;
  • timeline refreshes;
  • login attempts; and
  • media uploads.

For sensitive writes, use conservative local limits if the distributed limiter is unavailable. For reads, limited fail-open behavior may preserve availability.

Data protection and abuse

Encrypt data in transit and at rest. Do not log access tokens or tweet text unnecessarily. Apply stricter limits to accounts showing spam, automated follow churn, or duplicate posting patterns.

Abuse is a scaling problem too: malicious clients can generate more load than normal users.

26. Observability

Monitor the user journey, not only servers.

Important metrics

  • Home and post request rate, errors, and p50/p95/p99 latency;
  • Tweet Store commit latency and replication lag;
  • Redis hit rate, eviction rate, memory, and hot-key traffic;
  • Kafka publish errors, consumer lag, and oldest-event age;
  • fan-out writes per second and completion time;
  • hydration cache misses and filtered-candidate rate;
  • outbox backlog and oldest unpublished record; and
  • regional replication lag.

The most valuable end-to-end metric is:

Time from durable tweet creation until an eligible follower can retrieve it.

Call this tweet visibility lag. A healthy Kafka lag does not guarantee healthy timeline writes, so measure the complete path.

Logs and tracing

Use structured logs with request id, tweet id, event id, region, dependency latency, and retry reason. Do not log sensitive content by default.

Propagate a trace id into the outbox event and consumer. This lets us see where time was spent across a request and its later asynchronous work.

Alerts

Alert on SLO symptoms:

  • Home p99 above 200 ms;
  • tweet commit failures;
  • visibility lag above the allowed window;
  • growing outbox or Kafka lag;
  • Redis memory pressure;
  • hot partitions;
  • unusual authorization failures; and
  • regional replication problems.

27. Multi-region design

Assign each user a home region.

                 Global traffic manager
                    ┌──────┴──────┐
                    │             │
                Region A      Region B
              Alice's home   Bob's home
                    │             │
                    └── asynchronous ──┘
                        replication

When Bob posts:

  1. Route the write to Bob’s home region.
  2. Commit the tweet there.
  3. Publish the event locally.
  4. Replicate the tweet and event to other regions.
  5. Regional workers update local follower timelines.

Alice reads Home from her own region. If Bob lives elsewhere, his tweet may take slightly longer to arrive. That fits our follower-consistency requirement.

During a regional outage, serve replicated reads from another region. Before accepting writes there, fence the failed region so both regions cannot act as the writer for the same user. A region lease or epoch can establish one active owner.

Media uses globally replicated object storage and a CDN. Private account data may remain in a required geography for data-residency reasons.

28. Adding the remaining features

Media

Client
  ↓ request pre-signed URL
Object Storage
Scan/transcode workers
CDN

The Tweet record contains media ids, not image or video bytes.

Likes and retweets

Store (user_id, tweet_id) as the source of truth for likes. Keep a fast counter in Redis and reconcile it periodically.

A retweet can be a new tweet that references the original and uses the same delivery pipeline.

Consume tweet create and delete events into an inverted index such as OpenSearch. Search can lag by a few seconds without affecting tweet durability.

Notifications

Consume follow, like, reply, and mention events. Aggregate repetitive events before sending through APNs or FCM.

Ranking

Keep candidate generation separate from ranking:

Precomputed feed + celebrity tweets
           Candidates
         Ranking Service
             Top 20

A score might combine recency, engagement, and author affinity. Keeping this layer separate allows the model to change without redesigning tweet storage.

Live updates

Polling is simplest. If the product requires a “new tweets available” signal, Server-Sent Events are often enough because communication is one-way.

WebSockets add bidirectional messaging but also connection routing, heartbeats, reconnection, and backpressure. They do not replace the timeline API or solve feed fan-out by themselves.

29. Final architecture

WRITE
─────
Client
  → API Gateway
  → Tweet Service
  → Tweet Store + Outbox
  → Return 201
  → Kafka
  → Fan-out Workers
  → Home Feed Store

READ
────
Client
  → API Gateway
  → Timeline Service
  → Precomputed Feed
  + Celebrity Timelines
  → Merge
  → Fetch Tweet Objects
  → Check Visibility
  → Return Page

The main reasoning chain is:

Home must be fast
Reads greatly outnumber writes
Precompute feeds for ordinary authors
Celebrity fan-out creates extreme write amplification
Pull celebrity tweets during reads
Use a hybrid feed

Kafka makes fan-out asynchronous. Redis serves hot feed and tweet data. Tweets and follow records remain the source of truth. Timelines are derived and rebuildable.

30. Interview-ready summary

Key decisions to remember

  1. Start with the simple pull model before introducing infrastructure.
  2. Push ordinary-author tweets because Home is read-heavy.
  3. Pull celebrity tweets to avoid millions of writes from one post.
  4. Store tweet ids in feeds, then hydrate complete objects.
  5. Commit the tweet before acknowledging it.
  6. Use an outbox so a committed tweet cannot miss its Kafka event.
  7. Make fan-out idempotent because duplicate delivery is expected.
  8. Check current authorization during reads.
  9. Partition follower lists to handle high-degree accounts.
  10. Measure end-to-end tweet visibility lag.

Likely interviewer follow-up questions

  • How would you choose the celebrity threshold?
  • What happens when a user follows someone and immediately opens Home?
  • How do you delete a tweet from millions of feeds?
  • What happens when Kafka or Redis is unavailable?
  • How do you prevent duplicate tweets after a retry?
  • How do you preserve ordering?
  • How would you add ranking?
  • How would you support a user following 50,000 accounts?
  • How do protected accounts work with stale feed entries?
  • How would you fail over a region?

Senior-level points that differentiate the answer

  • Explain where eventual consistency is acceptable and where it is unsafe.
  • Distinguish source-of-truth records from derived feeds and caches.
  • Identify and solve the database/Kafka dual-write problem.
  • Treat duplicate and out-of-order events as normal.
  • Describe backpressure and a degraded read path when consumers lag.
  • Discuss hot keys and celebrity-specific isolation.
  • Fail closed when private visibility cannot be verified.
  • Monitor visibility lag rather than only queue lag.

A 1–2 minute verbal answer

I would scope Twitter to posting, following, profile timelines, and a reverse-chronological Following feed. At 250 million daily users and five Home loads per day, we have roughly 14,500 average and 72,500 peak feed reads per second, while tweet creation averages only about 1,700 writes per second. Since reads dominate, I want to precompute feeds.

Pure fan-out on write makes Home fast, but it creates tens of millions of writes for a celebrity tweet. I would therefore use hybrid fan-out: push tweet ids into follower feeds for ordinary authors, skip that work for celebrity authors, and merge their recent profile tweets during reads.

The Tweet Service durably stores the tweet and an outbox record before returning. An outbox relay publishes to Kafka, and idempotent fan-out workers update derived feeds asynchronously. Home reads a user’s precomputed ids, adds celebrity candidates, cursor-paginates, batch-fetches tweet objects, and enforces current privacy rules.

Tweets and follow edges are sources of truth; feeds and caches are rebuildable. I would partition tweets by hash of tweet id, author timelines by author and time bucket, and celebrity follower lists by author plus bucket. If Kafka is down, the outbox waits. If Redis is down, Home degrades to a durable feed store or bounded fan-out on read. The key operational metric is end-to-end tweet visibility lag.

For the broader interview framework around this problem, see the System Design Interview Complete Guide.