Fan-Out Patterns - Complete Deep Dive

Prerequisites: Message Queues, Caching Used in: Twitter Feed, Instagram, Notification System


What is Fan-Out?

Fan-out is distributing data from one source to many recipients. When a user posts content, how does it appear in their followers’ feeds? That distribution mechanism is “fan-out.”

Real-world analogy: A newspaper publisher.

User with 1000 followers posts a tweet.

Fan-out on write: write to 1000 follower timelines immediately
Fan-out on read: do nothing now, compute when each follower opens app

Fan-Out on Write (Push Model)

When a user publishes content, immediately write it to every follower’s feed/inbox.

flowchart LR
    A[User posts<br/>a tweet] --> B[Fan-Out Service] --> C[Write to N follower<br/>timelines cache/DB]

    classDef client fill:#f97316,stroke:#c2410c,color:#fff
    classDef service fill:#10b981,stroke:#065f46,color:#fff
    classDef data fill:#fbbf24,stroke:#92400e,color:#000
    class A client
    class B service
    class C data
Detailed flow:
  1. User A (1000 followers) posts "Hello World"
  2. Fan-out worker fetches follower list: [B, C, D, ... 1000 users]
  3. For each follower, push tweet to their pre-computed timeline:
     Redis: ZADD timeline:B <timestamp> <tweet_id>
     Redis: ZADD timeline:C <timestamp> <tweet_id>
     Redis: ZADD timeline:D <timestamp> <tweet_id>
     ... (1000 writes)
  4. When follower B opens app:
     Redis: ZREVRANGE timeline:B 0 49  → instant! Already computed.

Timeline storage (Redis Sorted Set per user):
  timeline:userB
  Score (timestamp)  |  Value (tweet_id)
  1709123456         |  tweet_001
  1709123400         |  tweet_002
  1709123300         |  tweet_003
  ...
  (keep last 800 tweets, trim rest)

Pros:

Cons:


Fan-Out on Read (Pull Model)

Do nothing at write time. When a user opens their feed, fetch and merge posts from everyone they follow.

flowchart LR
    A[User opens<br/>their feed] --> B[Feed Service] --> C[Fetch posts from<br/>all N following]

    classDef client fill:#f97316,stroke:#c2410c,color:#fff
    classDef service fill:#10b981,stroke:#065f46,color:#fff
    class A client
    class B,C service
Detailed flow:
  1. User B opens app, requests feed
  2. Feed service fetches B's following list: [A, X, Y, Z, ... 500 users]
  3. For each followed user, fetch their recent posts:
     Get posts from A (last 50)
     Get posts from X (last 50)
     Get posts from Y (last 50)
     ... (500 queries!)
  4. Merge all posts, sort by timestamp
  5. Return top 50 to user B

  Latency: depends on how many users B follows
           500 users × 1ms per query = 500ms (too slow!)
           Optimized with parallel fetches: ~50-100ms

Pros:

Cons:


Hybrid Approach (What Twitter/Instagram Actually Use)

Combine both: use fan-out-on-write for regular users, fan-out-on-read for celebrities.

flowchart TD
    A[User Posts a Tweet] --> B{Celebrity? > 10K followers}
    B -->|NO| C[Fan-out on Write]
    C --> D[Push to all follower timelines]
    B -->|YES| E[Store in Celebrity Posts Only]
    E --> F[Do NOT fan-out to followers]

    G[User Reads Feed] --> H[1. Fetch pre-computed timeline from cache]
    H --> I[2. Fetch celebrity posts they follow]
    I --> J[3. Merge + Rank + Return Top N]

    classDef service fill:#10b981,stroke:#065f46,color:#fff
    classDef data fill:#fbbf24,stroke:#92400e,color:#000
    classDef decision fill:#818cf8,stroke:#4338ca,color:#fff
    class B decision
    class C,D service
    class E,F data
    class H,I,J service
Example:
  User B follows: [Alice(200 followers), Bob(500), Taylor(50M)]

  When Alice posts:
    → Fan-out on write: push to all 200 follower timelines (incl. B)

  When Taylor posts:
    → No fan-out. Store in Taylor's post list only.

  When User B opens feed:
    → Fetch pre-computed timeline (has Alice's and Bob's posts)
    → Fetch Taylor's recent posts (fan-out on read for celebrity)
    → Merge and return

The Celebrity Problem (Hot Partition)

The core challenge that makes fan-out interesting:

Regular user (1000 followers):
  Posts a tweet → fan-out to 1000 timelines → done in ~1 second
  Cost: 1000 Redis writes = cheap

Celebrity (50 million followers):
  Posts a tweet → fan-out to 50M timelines → takes MINUTES!
  Cost: 50M Redis writes = extremely expensive
  Problem 1: massive write amplification
  Problem 2: followers see tweet minutes late
  Problem 3: single tweet consumes enormous system resources

Solutions

Approach How Trade-off
Hybrid model Skip fan-out for celebrities, merge at read time Slightly slower reads
Tiered fan-out Prioritize active followers first, fan-out to inactive later Complexity
Selective fan-out Only fan-out to online/recently-active users May miss some users
Async with priority Celebrity posts get lower priority in fan-out queue Eventual delivery

Comparison Table

Feature Fan-Out on Write Fan-Out on Read Hybrid
Write speed Slow (N writes per post) Fast (1 write) Medium
Read speed Very fast (pre-computed) Slow (aggregate on demand) Fast
Storage High (copies everywhere) Low (stored once) Medium
Celebrity handling Terrible (50M writes) Good Good
Inactive users Wasted writes No waste Configurable
Consistency High (feed ready instantly) Depends on merge window High
Complexity Low (read path simple) High (complex merge) High
Best for Most social feeds Celebrity-heavy feeds Production systems

Architecture for News Feed (Complete)

flowchart TD
    subgraph WritePath["WRITE PATH"]
        A[Client] --> B[API GW]
        B --> C[Post Service]
        C --> D[Post DB]
        C --> E[Kafka]
        E --> F[Fan-Out Worker 1]
        E --> G[Fan-Out Worker 2]
        F --> H[Timeline Cache<br/>Redis]
        G --> H
    end

    subgraph ReadPath["READ PATH"]
        I[Client] --> J[API GW]
        J --> K[Feed Service]
        K --> L[Timeline Cache<br/>pre-computed]
        K --> M[Celebrity Posts<br/>pull]
        L --> N[Merge + Rank + Return]
        M --> N
    end

    classDef client fill:#f97316,stroke:#c2410c,color:#fff
    classDef service fill:#10b981,stroke:#065f46,color:#fff
    classDef data fill:#fbbf24,stroke:#92400e,color:#000
    classDef async fill:#818cf8,stroke:#4338ca,color:#fff
    class A,I client
    class B,C,F,G,J,K,N service
    class D,H,L,M data
    class E async

When to Use / When NOT to Use

Fan-Out on Write:

Fan-Out on Read:

Hybrid:


Real-World Examples

Company Approach Details
Twitter Hybrid Fan-out on write for < 10K followers. Pull for celebrities. Timeline stored in Redis.
Instagram Hybrid Similar to Twitter. Machine learning ranking applied at read time.
Facebook Fan-out on read (mostly) Complex ranking model requires read-time aggregation. Edge Rank algorithm.
LinkedIn Fan-out on write Feed pre-computed for faster load. Network smaller than Twitter/FB.
TikTok Fan-out on read Algorithm-driven, not follow-based. Content fetched by recommendation model at read time.

Common Interview Questions

Q: “How would you design a news feed system?” A: Hybrid fan-out. On write: publish post → fan-out workers push to follower timelines (Redis sorted sets) for regular users. Skip celebrities. On read: fetch pre-computed timeline from cache + pull recent celebrity posts + merge + rank + return top 50. Use Kafka to decouple write from fan-out.

Q: “How do you handle the celebrity problem?” A: Define a threshold (e.g., > 10K followers). Above threshold, don’t fan out on write — store post in celebrity’s own list only. On read, merge celebrity posts with the pre-computed timeline. This limits write amplification to at most 10K writes per post.

Q: “What’s the storage requirement for pre-computed timelines?” A: Store only tweet IDs (not full tweets). Each entry: 8 bytes (tweet_id) + 8 bytes (timestamp) = 16 bytes. Keep last 800 tweets per user. 200M DAU × 800 × 16B = ~2.5 TB in Redis. Expensive but feasible with Redis Cluster.

Q: “How do you handle a user unfollowing someone?” A: For fan-out on write: don’t immediately remove old posts from timeline (expensive). Let them age out naturally as new posts push them down. For new posts, the unfollowed user’s tweets simply stop being fanned out. Eventually the timeline contains none of their content.

Q: “Fan-out on write or read for a notification system?” A: Fan-out on write (push). Notifications are time-sensitive and infrequent per user. When an event triggers a notification (new follower, like, comment), immediately write to the recipient’s notification inbox. Users check notifications infrequently, so pre-computing ensures instant load when they do.


← Back to Fundamentals Next: Event Sourcing →

💬 Comments