The news feed question is really one question — push or pull — and everything else follows from how you answer it. It underpins Twitter, Instagram and Reddit, so it is worth learning once, properly.
Requirements
Functional
- Publish a post; see a feed of posts from the people you follow.
- Feed ordered by recency or relevance.
- Infinite scroll.
Non-functional
- Feed loads in under ~200 ms; it is the app's home screen.
- Eventual consistency is fine — a post appearing a few seconds late is acceptable.
- Read-heavy: roughly 100 reads per write.
300M daily users, each opening the feed 10× per day → ~35,000 feed reads/second
2M posts/day → ~25 posts/second
Average 200 followers per user → 5,000 feed writes/second on fanoutFanout on read (pull)
When a user opens the feed, fetch the people they follow, query recent posts from each, merge and sort.
GET /feed
→ followees = follows(user) (500 ids)
→ posts = for each followee: latest N posts
→ merge sorted by time, take 20- Writing a post is O(1) — one row.
- Reading is expensive — hundreds of queries and a
k-way merge on every open, on the latency-critical path.
- No wasted work for users who never log in.
Fanout on write (push)
When someone posts, immediately append the post id into every follower's precomputed feed list.
POST /post
→ store the post
→ for each of my 200 followers:
LPUSH feed:{follower_id} post_id
LTRIM feed:{follower_id} 0 999 (keep the newest 1,000)- Reading is O(1) — one cache read of a ready list. This is the win.
- Writing is O(followers) — and asynchronous, via a queue, so the poster is not kept
waiting.
- Wasted work for inactive followers.
The celebrity problem
Fanout on write collapses when someone has 100M followers: one post becomes 100M list writes, taking minutes and swamping the queue. This is the crux of the question.
The hybrid, which is the expected answer:
- Normal users (below some follower threshold — tens of thousands) → fanout on write.
- Celebrities → no fanout. Their posts are pulled at read time.
- A feed read = the precomputed list merged with a live query for the handful of
celebrities the user follows.
Nearly every user's feed is one cache read plus a small merge, and no single post ever causes a hundred million writes. Twitter and Instagram both work roughly this way.
Storage
posts post_id, author_id, content, media_urls, created_at
follows follower_id, followee_id, created_at (indexed both directions)
feed cache feed:{user_id} → list of post_ids (Redis, capped at ~1,000)Store ids in the feed, not post content. A post that is edited or deleted would otherwise need rewriting in millions of lists. Hydrate the ids into full posts from a separate post cache on read — one extra batch lookup, and a single source of truth.
The follows table is queried in both directions — "who do I follow" for reads and "who follows me" for fanout — so it needs an index each way, or two tables.
Ranking
Chronological is the simple answer and a legitimate one. If asked for relevance, describe the shape rather than inventing a model: candidate generation (recent posts from your graph, plus some recommended), then scoring by a model over features — author affinity, engagement rate, recency decay, content type — then re-ranking for diversity so one author does not fill the screen. Precompute candidates asynchronously; scoring at read time on a few hundred candidates is affordable, scoring millions is not.
Pagination
Offset pagination breaks on a live feed: new posts arriving shift everything down, so page
2 repeats items from page 1. Use cursor pagination — "give me the 20 items older than
this post id / timestamp" — which is stable regardless of what arrives above it, and is also what lets the query use an index directly.
Follow-ups to expect
- A new follow — backfill that person's recent posts into the follower's feed, or let
the merge pick them up.
- Deletes and edits — the id-not-content decision above makes this trivial.
- Read consistency for your own posts — a user must see their own post immediately, so
merge their recent posts in locally rather than waiting for fanout.
- Feed for a user who follows nobody — a cold start; fall back to trending or
recommended content.
- Media — store in blob storage and serve via CDN; the feed carries URLs.
- Hot partition — a celebrity's post row is read enormously; cache it aggressively at
the edge.
Track this problem on the System Design sheet.
Frequently asked
What is the difference between fanout on write and fanout on read?
Fanout on write pushes each new post into every follower's precomputed feed list, making reads a single O(1) cache lookup and writes O(followers). Fanout on read stores the post once and assembles each feed by querying everyone the user follows at read time — cheap writes, expensive reads. Most systems use fanout on write because feeds are read far more often than posts are made.
How do you handle celebrity accounts in a news feed?
With a hybrid. Users below a follower threshold use fanout on write; accounts above it are excluded from fanout entirely, and their posts are fetched live when a follower reads their feed. The feed becomes the precomputed list merged with a small live query, so no single post ever triggers a hundred million writes.
Should the feed store post IDs or full posts?
IDs. Storing full content means an edit or delete has to be rewritten in millions of feed lists, and it multiplies storage. Store ids, then hydrate them from a post cache in one batch lookup at read time — one source of truth, and edits are instantly reflected everywhere.