DrawLintDrawLint.ai
🗺️Design Patterns·6 min read

Fan-out on Write vs Read

How social feeds are built — and the hybrid that handles celebrities with millions of followers.

Fan-out on write and fan-out on read are the two classic ways to build a social feed, notification inbox, activity stream, or personalized timeline. The decision is simple to state: do the work when someone publishes, or do the work when someone opens the feed?

🔭Think of it like…
Fan-out on write is printing a newsletter and dropping a copy into every subscriber mailbox immediately. Reading is instant, but publishing is expensive. Fan-out on read is keeping one master copy at the front desk; each reader gets a custom packet assembled only when they arrive. Publishing is cheap, but each read does more work.

The problem: timeline reads and writes scale differently

A feed is a many-to-many problem. One author has many followers, and one reader follows many authors. If Alice follows 800 people, her home feed needs to merge recent posts from 800 sources. If a celebrity has 200 million followers, one post may need to appear in 200 million feeds. Neither extreme is safe to handle naively.

the same post can be paid for at write time or read time
// Fan-out on write: pay when the author publishes.
POST /posts
  save post p
  for follower in followers(author):
    LPUSH timeline:{follower} p

GET /home
  return LRANGE timeline:{viewer} 0 499

// Fan-out on read: pay when the viewer opens the feed.
GET /home
  authors = following(viewer)
  posts = fetchRecentPosts(authors)
  return mergeRankAndTrim(posts)

The right answer depends on read/write ratio, follower distribution, freshness requirements, ranking complexity, and acceptable storage duplication. Production social systems usually combine both models.

Fan-out on write: push into precomputed timelines

In the push model, publishing a post triggers a background job that inserts that post ID into each follower timeline. Reads are then cheap: load the viewer timeline from Redis or a feed store, hydrate the post IDs, rank or filter if needed, and return the page.

  • Fast reads: the expensive follower expansion already happened, so opening the app can be a single timeline lookup.
  • Write amplification: one post becomes many timeline writes. A user with 5,000 followers creates 5,000 feed insertions.
  • Storage duplication: the same post ID is copied into many precomputed lists. Usually this is acceptable because lists store IDs, not the full post body.
precomputed timeline in Redis
// Author 42 publishes post 9001.
followers = [7, 8, 9, ...]

for followerId in followers:
  LPUSH home_timeline:{followerId} 9001
  LTRIM home_timeline:{followerId} 0 999

// Viewer 7 opens the app.
postIds = LRANGE home_timeline:7 0 49
posts = MGET post:{id} for id in postIds
Cap the timeline
Feed caches are usually capped to the most recent few hundred or few thousand IDs. Infinite history lives in the post store; the timeline cache is a fast recent window.

Fan-out on read: assemble when the viewer asks

In the pull model, publishing saves the post once. When a viewer opens the feed, the service fetches recent posts from everyone the viewer follows, merges them by time and ranking features, filters blocked or muted authors, and returns the top items.

DimensionFan-out on write / pushFan-out on read / pull
Post creationExpands to every follower timelineWrites one post record only
Home feed readReads a precomputed list, then hydrates postsFetches many author streams, merges, ranks, and trims
Best forNormal users with bounded follower counts and high read volumeAuthors with huge audiences or systems with rare reads
Main failure modeCelebrity post creates massive write amplificationViewer following many authors creates slow feed assembly
StorageDuplicates post IDs across follower timelinesStores each post once, but spends CPU on reads

Pull is attractive for long-tail authors and cold feeds. If most users rarely open the app, pushing every post to every inactive timeline wastes work. Pull delays the cost until a human actually asks for the feed.

The celebrity problem and the hybrid answer

Pure push breaks on hot authors. A celebrity, brand, or emergency alert account with 200 million followers cannot synchronously write one post into 200 million timelines. Even if the writes are asynchronous, the queue backlog, Redis pressure, and storage churn can overwhelm the system.

The common production fix is a hybrid feed. Push normal authors into follower timelines. Do not push celebrity or hot-user posts into every follower timeline. Instead, keep celebrity posts in author streams and pull them at read time for viewers who follow those authors.

hybrid feed read
function readHome(viewer):
  // Precomputed push timeline for normal authors.
  base = redis.lrange("home_timeline:" + viewer, 0, 499)

  // Small set: celebrity accounts this viewer follows.
  hotAuthors = getHotAuthorsFollowedBy(viewer)
  hotPosts = []
  for author in hotAuthors:
    hotPosts += redis.lrange("author_posts:" + author, 0, 20)

  return rankAndMerge(base, hotPosts).take(50)
Bound the expensive side
The hybrid makes one celebrity post cheap to publish and keeps normal feed reads fast. Read time pays only for the small number of hot authors the viewer follows, not for everyone in the graph.

This is also where two-stage fan-out appears: a lightweight first pass delivers IDs quickly, and a second stage enriches, ranks, or backfills. Hot celebrity streams often need hot-key mitigation because millions of readers may request the same author feed.

Ranking, freshness, and consistency gotchas

  • Deletes and privacy changes: a deleted post may already exist in many precomputed timelines. Reads must hydrate by ID and re-check visibility, or a cleanup job must remove stale references.
  • Follow and unfollow: when a viewer follows someone new, you may backfill recent posts into the timeline. On unfollow, you may lazily filter those posts at read time rather than remove every old entry immediately.
  • Ranking changes: precomputed chronological lists are easy to cache. Machine-learned ranking may still need read-time features, hydration, and reordering.
  • Duplicate posts: hybrid feeds can see the same post from base timeline and hot-author pull. Deduplicate by post ID before ranking.
  • Backpressure: fan-out workers must be rate-limited. If queues grow, degrade gracefully by delaying low-priority fan-out or switching selected authors to pull mode temporarily.

Real-world examples

Social networks, team chat, notification centers, activity streams, and news feeds all use this trade-off. Twitter-like timelines often push ordinary users and pull celebrities. A chat app may push unread counters and recent message IDs into per-user inboxes. A notification system may push critical alerts but pull low-priority activity on demand.

SystemLikely patternReason
Home timelineHybrid push plus pullFast reads for normal graph; celebrity posts avoid write storms
Email inboxWrite-time deliveryEach message belongs in recipient mailboxes and reads must be fast
Analytics activity feedRead-time assemblyReads are rare and freshness can be computed on demand
Key takeaways
  • Fan-out on write pushes each post into follower timelines at publish time, making reads fast but writes expensive.
  • Fan-out on read stores the post once and assembles the feed on demand, making writes cheap but reads expensive.
  • Pure push breaks for celebrities because one post can trigger millions of timeline writes.
  • Production feeds are usually hybrid: push normal authors, pull hot authors at read time, then merge and rank.
  • Precomputed Redis timelines are fast recent windows; reads still need hydration, visibility checks, deduplication, and ranking.
One post expands into a write for every follower timeline. At celebrity scale, that creates enormous queue backlog, cache pressure, storage churn, and delayed delivery. The hybrid skips that push and pulls celebrity posts during feed reads.
It fits when reads are rare, audiences are huge, or writing into every potential recipient timeline would waste more work than assembling the feed on demand. It also fits hot authors whose posts would overload the push pipeline.
Load the precomputed timeline for normal pushed authors, fetch recent posts for hot authors the viewer follows, merge the two sets, deduplicate by post ID, apply visibility checks, rank, and return the top page.
Finished this lesson?

Mark it complete to track your progress through the workbook.