Skip to content
advancedPhase 52 · HLD Case Studies

Twitter / Social Feed

Design Twitter's timeline, tweet, and social graph system.

2h
0 problems
Topic Progress0%

Requirements & Scope

Functional Requirements

The core features of a Twitter-like system:

  • Post Tweet: Users create tweets (text up to 280 characters, optional media attachments)
  • Follow/Unfollow: Users follow other users to see their tweets in their timeline
  • View Timeline: Users see a chronological or ranked feed of tweets from people they follow
  • Search Tweets: Users search tweets by keywords, hashtags, or user handles
  • Trending Topics: Display currently popular hashtags/topics based on tweet volume
  • Like/Retweet: Users can engage with tweets

Non-Functional Requirements

Requirement Target Rationale
Timeline Latency < 500ms Users expect near-instant feed refresh
Availability 99.99% Social media is always-on; downtime damages engagement
Consistency Eventual consistency OK A 2-5 second delay for new tweets in timeline is acceptable
Throughput 600K tweets/sec write, 600K timeline reads/sec Based on Twitter-scale numbers
Durability Tweets must never be lost Users expect data persistence
Scalability 300M+ monthly active users Must scale horizontally

Core Entities

User: { user_id, username, display_name, email, created_at }
Tweet: { tweet_id, user_id, content, media_urls, created_at }
Follow: { follower_id, followee_id, created_at }
Timeline: { user_id, tweet_ids[], last_updated }

Capacity Estimation

Write load: 500M tweets/day = ~5,800 tweets/sec avg, ~12,000 tweets/sec peak
Read load: Each user views timeline ~10 times/day = 3B reads/day = ~35K reads/sec avg
Storage: 500M tweets/day * 1KB avg = 500GB/day = ~180TB/year
Bandwidth: ~1.8TB/day write, ~3.6TB/day read (including metadata)

Interview Tips

  • Start by clarifying scope: "Are we building for mobile and web? Do we need DM support?"
  • Ask about scale: "Is this Twitter-scale (300M users) or a smaller system?"
  • Confirm timeline ordering: "Chronological or algorithmic ranking?"

Data Model & Storage

Data Model Design

Users Table

CREATE TABLE users (
    user_id     BIGINT PRIMARY KEY,
    username    VARCHAR(30) UNIQUE NOT NULL,
    display_name VARCHAR(50),
    email       VARCHAR(255) UNIQUE,
    bio         VARCHAR(160),
    created_at  TIMESTAMP DEFAULT NOW()
);

Tweets Table (Cassandra)

CREATE TABLE tweets (
    tweet_id    BIGINT,  -- Snowflake ID for time-ordered uniqueness
    user_id     BIGINT,
    content     TEXT,
    media_urls  LIST<TEXT>,
    retweet_of  BIGINT,  -- NULL if original tweet
    reply_to    BIGINT,  -- NULL if not a reply
    created_at  TIMESTAMP,
    PRIMARY KEY (tweet_id)
);

-- Query pattern: get user's tweets in reverse chronological
CREATE TABLE user_tweets (
    user_id    BIGINT,
    tweet_id   BIGINT,
    created_at TIMESTAMP,
    PRIMARY KEY (user_id, created_at, tweet_id)
) WITH CLUSTERING ORDER BY (created_at DESC);

Follows Table

CREATE TABLE follows (
    follower_id  BIGINT,
    followee_id  BIGINT,
    created_at   TIMESTAMP,
    PRIMARY KEY (follower_id, followee_id)
);

-- Reverse lookup: who follows this user
CREATE TABLE followers (
    followee_id  BIGINT,
    follower_id  BIGINT,
    created_at   TIMESTAMP,
    PRIMARY KEY (followee_id, follower_id)
);

Timeline Cache (Redis)

Key: timeline:{user_id}
Type: Sorted Set
Members: tweet_ids
Scores: timestamp (epoch millis)
Max length: ~800 tweets per user

Storage Selection Rationale

Data Store Reason
User profiles MySQL/PostgreSQL Relational, ACID for account data
Tweets Cassandra Write-heavy, append-only, time-partitioned
Follow graph MySQL + Redis relational for queries, cache for fanout
Timelines Redis In-memory sorted sets for fast reads
Search index Elasticsearch Full-text search with inverted index
Media S3 + CDN Object storage with edge caching

ID Generation

Use Twitter Snowflake IDs:

| 1 bit (unused) | 41 bits (timestamp) | 10 bits (machine) | 12 bits (sequence) |

= 64-bit integer, time-ordered, globally unique, no coordination needed

Denormalization Strategy

  • Store denormalized username and display_name with each tweet to avoid joins
  • Pre-compute follower counts
  • Timeline is fully denormalized: just a list of tweet IDs in order

Feed Generation

Timeline Generation Approaches

Option 1: Fanout on Write (Push Model)

When a user posts a tweet, immediately push it to all followers' timelines.

User A posts tweet
    |
    v
Fanout Service
    |
    +--> Follower 1's timeline
    +--> Follower 2's timeline
    +--> Follower 3's timeline
    +--> ... (all followers)

Pros:

  • Timeline read is O(1) — just fetch from pre-computed list
  • Read latency is very low (< 50ms)
  • Simple read path

Cons:

  • Write amplification: posting to 1000 followers = 1000 writes
  • High write load for popular users
  • Wasted work if followers never visit their timeline

Option 2: Fanout on Read (Pull Model)

When a user views their timeline, fetch latest tweets from all followees on the spot.

User A opens timeline
    |
    v
Timeline Service
    |
    +--> Fetch latest from User X
    +--> Fetch latest from User Y
    +--> Fetch latest from User Z
    +--> Merge and rank
    +--> Return top 100

Pros:

  • No write amplification
  • Always shows freshest content
  • No wasted computation

Cons:

  • Read latency is high: following 1000 users = 1000 queries
  • Slow for users who follow many accounts
  • Computationally expensive at read time

Read/Write Path

Write Path

1. User posts tweet via API
2. Tweet Service validates and stores in Cassandra
3. Assigns Snowflake ID
4. Publishes event to Fanout Service (Kafka)
5. Fanout Service pushes tweet_id to all followers' Redis timelines

Read Path

1. User requests timeline
2. Timeline Service reads from Redis sorted set
3. Fetches tweet details from Cassandra (batch get)
4. Optional: applies ranking model
5. Returns hydrated timeline to client

Timeline Hydration

Raw timeline from Redis: [tweet_105, tweet_104, tweet_102, tweet_99, ...]
                    |
                    v
Batch fetch from Cassandra:
  tweet_105 -> { content: "Hello world", user: "alice", ... }
  tweet_104 -> { content: "Great day!", user: "bob", ... }
  tweet_102 -> { content: "Check this out", user: "carol", ... }
                    |
                    v
Return hydrated timeline with full tweet objects

Fanout & Scaling

Fanout Service Architecture

Message Queue Based Fanout

Tweet Service --> Kafka Topic: "new_tweets"
                        |
                        v
              Fanout Workers (Consumer Group)
              |    |    |    |    |
              v    v    v    v    v
           Redis Redis Redis Redis Redis  (fan-out to follower timelines)

Fanout Worker Pseudocode

def fanout_tweet(tweet, user):
    follower_ids = get_follower_ids(user.id)  # From Redis set
    
    pipeline = redis.pipeline()
    for follower_id in follower_ids:
        pipeline.zadd(
            f"timeline:{follower_id}",
            {tweet.id: tweet.created_at.timestamp()}
        )
        pipeline.zremrangebyrank(f"timeline:{follower_id}", 0, -801)  # Keep last 800
    
    pipeline.execute()  # Atomic batch write

Scaling Strategies

Horizontal Scaling of Fanout Workers

  • Partition by tweet_id or user_id
  • Each worker handles a subset of users
  • Kafka consumer groups enable automatic partition assignment
  • Scale from 10 to 1000 workers as load increases

Redis Cluster Sharding

User ID % 16384 --> Redis Shard

Shard 0: timelines for user_ids 0-1023
Shard 1: timelines for user_ids 1024-2047
...
Shard 15: timelines for user_ids 15360-16383

Cassandra Partitioning

  • Partition tweets by tweet_id (Snowflake ID already time-ordered)
  • Use user_tweets table for user-specific queries
  • Replication factor: 3 across multiple availability zones

Handling Hot Keys

If a celebrity tweets, millions of fanout writes hit simultaneously:

  1. Rate limit fanout writes per second per worker
  2. Use write buffering: batch 1000 timeline writes per Redis call
  3. Prioritize active users: fanout to users who logged in last 24h first
  4. Defer inactive users: fanout to inactive users asynchronously, potentially skip
  5. Hybrid approach: use pull for celebrity followers (see next chapter)

Monitoring & Metrics

Key metrics to track:
- fanout_latency_p99: target < 2s for 99% of tweets
- fanout_queue_depth: messages waiting to be processed
- timeline_read_latency_p95: target < 500ms
- redis_memory_usage: alert at 80% capacity
- cassandra_write_latency_p99: target < 10ms
- tweet_throughput: tweets/sec being published

The Celebrity Problem

Problem Definition

A celebrity (e.g., @elonmusk with 150M followers) posting a tweet triggers fanout to 150M users. This is:

  • ~150M Redis writes for a single tweet
  • ~300TB of timeline storage if each tweet ID is 8 bytes
  • Minutes of fanout delay — other users' timelines are blocked
  • Cascading failures — Redis and Kafka become bottlenecks

Hybrid Fanout Approach

Key Insight

Divide users into two categories based on follower count:

if user.follower_count < CELEBRITY_THRESHOLD (e.g., 10,000):
    use fanout-on-write (push)
else:
    use fanout-on-read (pull) for that user's tweets

Architecture

                    +-- Normal User tweets --> Fanout Service --> Push to followers' timelines
Tweet Service ------|
                    +-- Celebrity tweets --> Stored in Cassandra only (no fanout)
                                              |
                                              v
                              Timeline Service pulls from celebrity on read

Timeline Read with Hybrid

def get_timeline(user_id):
    # 1. Get pre-computed timeline from Redis (push-based tweets)
    pre_computed = redis.zrevrange(f"timeline:{user_id}", 0, 199)
    
    # 2. Get recent tweets from followed celebrities (pull-based)
    celebrity_tweets = []
    for celeb_id in get_followed_celebrities(user_id):
        tweets = cassandra.query(
            "SELECT * FROM user_tweets WHERE user_id = %s LIMIT 10",
            celeb_id
        )
        celebrity_tweets.extend(tweets)
    
    # 3. Merge and sort by timestamp
    all_tweets = merge_sorted(pre_computed, celebrity_tweets)
    
    return all_tweets[:200]  # Return top 200

Threshold Tuning

Follower Count Strategy Rationale
< 1,000 Push Low fanout cost, instant read
1,000 - 10,000 Push Moderate cost, still acceptable
10,000 - 1M Hybrid Pull for this user's tweets
> 1M Pull only Never fanout; always pull

Optimizations for Celebrities

  1. Cache celebrity tweets separately with shorter TTL (30s vs 5min)
  2. Pre-fetch celebrity tweets when user opens app (background thread)
  3. Limit celebrity pull to last N tweets (5-10 per celebrity)
  4. Use read replicas for celebrity tweet queries (read-heavy workload)
  5. CDN cache for celebrity profiles and tweet details

Edge Cases

  • User crosses threshold: When a user grows from 9,999 to 10,000 followers, migrate their existing pushed timelines to pull-based
  • Viral tweet: A normal user's tweet goes viral — monitor and switch to pull if it exceeds threshold in real-time
  • Unfollow during fanout: Race condition between fanout and unfollow — use idempotent operations

Key Interview Points

  • Always mention the celebrity problem — it shows depth of understanding
  • Explain the tradeoff: push gives faster reads but higher write amplification
  • Discuss how the threshold can be tuned based on system load
  • Mention that most users are NOT celebrities (power law distribution)
  • The hybrid approach is the industry standard (used by Twitter, Facebook, LinkedIn)

Practice Problems

0/2solved
Design Tweet Search

Given a tweet store, design a search system that supports keyword search, hashtag search, and @mention search with sub-second latency.

Trending Topics

Design the trending topics feature that shows the top 10 trending hashtags in real-time across different regions.

Quiz

1. Why is fanout-on-write problematic for celebrity accounts?

Question 1 options

2. What is the primary advantage of fanout-on-read over fanout-on-write?

Question 2 options

3. Which database is best suited for storing tweets in a write-heavy Twitter system?

Question 3 options

4. What ID generation strategy does Twitter use to ensure time-ordered, globally unique tweet IDs?

Question 4 options

5. In the hybrid fanout approach, what happens when a user views their timeline?

Question 5 options

6. Why is Redis used for timeline storage rather than a traditional database?

Question 6 options

7. What is the recommended follower count threshold for switching from push to pull in the hybrid approach?

Question 7 options

8. How does Cassandra handle the time-series nature of tweets effectively?

Question 8 options

Flashcards

Question

What is fanout-on-write (push model) in a social feed system?

Answer

When a user posts content, it is immediately pushed/pre-computed to all followers' timeline caches. Reads are fast (O(1) lookup) but writes are expensive (write amplification proportional to follower count). Good for users with few followers.

Question

What is fanout-on-read (pull model) in a social feed system?

Answer

When a user requests their timeline, the system fetches latest content from all followees on-demand. No write amplification but reads are slow (must query all followees). Good for celebrities whose tweets should not be pushed to millions.

Question

What is the celebrity problem in Twitter's design?

Answer

A celebrity with millions of followers posting a tweet triggers fanout to millions of users, causing massive write amplification, high latency, and potential cascading failures. Solved by hybrid fanout: push for normal users, pull for celebrity tweets.

Question

Why use Snowflake IDs instead of auto-increment or UUIDs?

Answer

Snowflake IDs are 64-bit, time-ordered (enabling efficient range queries on timelines), globally unique (no coordination needed), and can be generated at high throughput across distributed machines without a central authority.

Question

Why is Cassandra preferred over MySQL for tweet storage?

Answer

Cassandra excels at write-heavy, append-only workloads with its LSM-tree engine. It scales horizontally, handles time-partitioned data efficiently, and supports high throughput with tunable consistency. MySQL struggles with write-heavy workloads at Twitter scale.

Question

How does Redis sorted set enable fast timeline retrieval?

Answer

Redis ZREVRANGE on a sorted set retrieves the N most recent items in O(log(N)) time. Tweet IDs are scored by timestamp, so fetching the latest 100 tweets is a single in-memory operation with sub-millisecond latency, critical for the <500ms SLA.

Question

What is the hybrid fanout approach?

Answer

Combines push and pull: fanout-on-write for users with <10K followers (manageable write cost), fanout-on-read for celebrities (avoid massive fanout). At read time, merge pre-computed push timelines with freshly pulled celebrity tweets.

Question

What is the write path when a user posts a tweet?

Answer

1) Validate and store tweet in Cassandra with Snowflake ID. 2) Publish event to Kafka topic. 3) Fanout workers consume events and push tweet_id to followers' Redis timeline sorted sets. 4) Fanout is idempotent and retries on failure.

Question

What is the read path when a user opens their timeline?

Answer

1) Fetch pre-computed tweet IDs from Redis sorted set (ZREVRANGE). 2) For followed celebrities, pull recent tweets from Cassandra. 3) Merge both sets and sort by timestamp. 4) Batch-fetch full tweet details from Cassandra. 5) Return hydrated timeline.

Question

Why is eventual consistency acceptable for Twitter timelines?

Answer

A 2-5 second delay in seeing a new tweet in your timeline does not significantly harm user experience. The trade-off allows the system to be highly available and partition-tolerant (AP in CAP theorem), which is more important for a social media platform.

Revision Notes

Key Takeaways

  • 1.Twitter's core challenge is generating personalized timelines at scale with sub-500ms latency
  • 2.Fanout-on-write gives fast reads but massive write amplification; fanout-on-read avoids writes but is slow at read time
  • 3.The hybrid approach (push for normal users, pull for celebrities) is the industry standard solution
  • 4.Cassandra's write-optimized LSM-tree and time-partitioned model make it ideal for tweet storage
  • 5.Redis sorted sets enable sub-millisecond timeline retrieval, critical for meeting latency SLAs
  • 6.Snowflake IDs provide time-ordered, globally unique IDs without coordination between distributed nodes
  • 7.The celebrity problem (power-law distribution of followers) is the key scaling challenge in feed systems
  • 8.Eventual consistency is an acceptable trade-off for social media's availability requirements

Interview Tips

  • Start every design by clarifying scope: scale, features, latency requirements — don't assume
  • Always mention the celebrity problem proactively — it demonstrates deep understanding of scaling
  • Draw the write path and read path separately — interviewers want to see you think through both
  • Explain WHY you chose each technology (Cassandra for writes, Redis for reads) — not just what
  • Discuss trade-offs explicitly: push vs pull, consistency vs availability, pre-compute vs on-demand
  • Mention monitoring and failure modes — it shows production-minded thinking
  • If asked to optimize, suggest: CDN for media, background pre-fetching, and caching layers
  • Practice the capacity estimation — being able to calculate tweets/day and storage/year impresses interviewers

Cheat Sheet

Twitter System Design - Cheat Sheet

Functional Requirements

  • Post tweet (text + media), follow/unfollow, view timeline, search, trending, like/retweet

Non-Functional Requirements

  • Timeline < 500ms, 99.99% availability, eventual consistency OK, 600K reads/sec, 500M tweets/day

Architecture Components

Component Purpose
Tweet Service Validate & store tweets in Cassandra
Fanout Service Push tweet_ids to followers' Redis timelines
Timeline Service Read from Redis + pull celebrity tweets, hydrate
Search Service Index tweets in Elasticsearch, handle queries
Trending Service Count hashtags with Count-Min Sketch, rank with time-decay

Storage Choices

Data Store Why
Tweets Cassandra Write-heavy, time-partitioned, append-only
Timelines Redis Sorted Sets Sub-ms reads, time-ordered retrieval
User profiles MySQL Relational, ACID for account data
Search Elasticsearch Full-text inverted index
Media S3 + CDN Object storage + edge caching

Fanout Strategies

Strategy When to Use Trade-off
Push (fanout-on-write) Normal users (<10K followers) Fast reads, expensive writes
Pull (fanout-on-read) Celebrities (>10K followers) Slow reads, no write amplification
Hybrid Production standard Best of both worlds

Celebrity Problem

  • Celebrity posting = millions of push writes = system overload
  • Solution: Don't fanout celebrity tweets; pull them at read time
  • Merge push-based timeline + pull-based celebrity tweets

Key Numbers

  • 500M tweets/day = ~5,800 tweets/sec avg
  • Snowflake ID: 41-bit timestamp + 10-bit machine + 12-bit sequence
  • Redis timeline: keep last 800 tweet IDs per user
  • Celebrity threshold: ~10,000 followers

Interview Flow

  1. Requirements (5 min) - functional + non-functional + scale estimation
  2. High-level design (10 min) - draw architecture, identify services
  3. Data model (10 min) - schema, storage selection rationale
  4. Deep dive: Feed generation (15 min) - fanout strategies, hybrid approach
  5. Deep dive: Celebrity problem (10 min) - explain hybrid with code
  6. Scaling & bottlenecks (10 min) - Redis cluster, Cassandra sharding, monitoring