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
usernameanddisplay_namewith 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_tweetstable for user-specific queries - Replication factor: 3 across multiple availability zones
Handling Hot Keys
If a celebrity tweets, millions of fanout writes hit simultaneously:
- Rate limit fanout writes per second per worker
- Use write buffering: batch 1000 timeline writes per Redis call
- Prioritize active users: fanout to users who logged in last 24h first
- Defer inactive users: fanout to inactive users asynchronously, potentially skip
- 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
- Cache celebrity tweets separately with shorter TTL (30s vs 5min)
- Pre-fetch celebrity tweets when user opens app (background thread)
- Limit celebrity pull to last N tweets (5-10 per celebrity)
- Use read replicas for celebrity tweet queries (read-heavy workload)
- 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
Given a tweet store, design a search system that supports keyword search, hashtag search, and @mention search with sub-second latency.
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?
2. What is the primary advantage of fanout-on-read over fanout-on-write?
3. Which database is best suited for storing tweets in a write-heavy Twitter system?
4. What ID generation strategy does Twitter use to ensure time-ordered, globally unique tweet IDs?
5. In the hybrid fanout approach, what happens when a user views their timeline?
6. Why is Redis used for timeline storage rather than a traditional database?
7. What is the recommended follower count threshold for switching from push to pull in the hybrid approach?
8. How does Cassandra handle the time-series nature of tweets effectively?
Flashcards
Question
What is fanout-on-write (push model) in a social feed system?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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
- Requirements (5 min) - functional + non-functional + scale estimation
- High-level design (10 min) - draw architecture, identify services
- Data model (10 min) - schema, storage selection rationale
- Deep dive: Feed generation (15 min) - fanout strategies, hybrid approach
- Deep dive: Celebrity problem (10 min) - explain hybrid with code
- Scaling & bottlenecks (10 min) - Redis cluster, Cassandra sharding, monitoring