The prompt
"Design the news feed" (Facebook, X, Instagram) is one of the most asked questions because the obvious design fails in two opposite ways: build feeds on write and celebrities break you; build them on read and every app open is a query against hundreds of accounts.
Snip Bio's app opens on a home feed: links posted by the creators you follow, newest first. There are 50 million daily users, each opening the app about 10 times a day; 5 million posts a day; most users follow 20 to 300 creators; most creators have a few hundred followers and a few have millions. The feed must load in under 200 ms.
1. Requirements
Functional: post a link (with text and an image); follow and unfollow; read the home feed, newest first, with infinite scroll.
Non-functional: feed loads in under 200 ms at p99; a new post appears in followers' feeds within a few seconds (a minute is acceptable for the largest creators); a deleted post disappears at once.
Out of scope: comments, likes, search, ads.
2. Estimates
| Value | What it decides | |
|---|---|---|
| Feed reads | 500 million a day ≈ 6,000 a second, about 20,000 at peak | Reads must be one cheap lookup |
| Posts | 5 million a day ≈ 60 a second | Writing a post is easy |
| Fan-out writes | 60 posts × 300 followers ≈ 18,000 feed inserts a second | Push is affordable for typical authors |
| One celebrity post | 5 million followers in one go | Push breaks; pull these authors |
| Feed storage | 50 million active users × 500 IDs × 8 bytes ≈ 200 GB | A Redis cluster, not a database table |
3. Entities and API
Post { id (Snowflake), author_id, text, image_key, created_at, deleted? }
Follow { follower_id, author_id, created_at } stored both ways (12.04)
Feed { user_id → list of post IDs, newest first, capped at 500 }
POST /posts { text, image_key } → 201 { id }
GET /feed?limit=20&before=ID → 200 { posts: [...], next: ID }
PUT /follows/{author_id} DELETE /follows/{author_id}Post IDs are Snowflake IDs (05.05 Distributed ID generation): unique, and ordered by time, so the cursor is just an ID.
4. High-level design
5 steps. Use Next and Back to follow the request.
Text version
- Posting writes one row and emits an event through the outbox (07.06). The image was already uploaded directly to object storage (13.01).
- Fan-out workers look up the author's followers in pages. For ordinary authors, they push the post ID onto each active follower's feed list, capped at 500.
- Authors with more than 100,000 followers are skipped: their posts are pulled at read time instead.
- Reading: the feed service takes the user's pushed IDs, adds recent posts from the few big authors they follow, and merges them newest first.
- It fetches the 20 posts it needs in one batch from the post cache, drops deleted ones and blocked authors, and returns them with a cursor.
- App → Post service
- Post service → Posts
- Posts → Fan-out workers: post.created
- Fan-out workers → Follow graph: followers
- Fan-out workers → Feed cache: push ID
- App → Feed service
- Feed service → Feed cache
- Feed service → Posts: batch get
5. Deep dives
Deep dive
Push, pull, or both?
Every app open queries up to 300 authors' recent posts and merges them: 20,000 reads a second times hundreds of lookups.
Reads are one list lookup. But a creator with 5 million followers makes 5 million writes per post, and the last followers see it minutes later.
Push for authors under a follower threshold, pull for the few above it, and merge at read time (12.03 Feed generation: push, pull, hybrid). Skip pushing to users inactive for 30 days; when one returns, build their feed by pulling, once.
Deep dive
What goes in a feed, and how it's read
Edits and deletions would have to be applied to millions of copies, and 200 GB becomes many terabytes.
Small feeds and fresh content, but 20 round trips per page: the N+1 problem.
One multi-get for the page's 20 posts and their authors. Deleted posts are filtered here, so deletion is immediate without touching any feed. Popular posts are hot keys; coalesce identical reads (06.05 Stampedes and request coalescing).
Deep dive
Infinite scroll while new posts arrive
Five new posts arrive while the user reads page 1; page 2 at offset 20 now starts five posts earlier, and they see five posts twice.
New posts no longer shift pages, but two posts in the same millisecond can be skipped or repeated at a page boundary.
Snowflake IDs are unique and time-ordered: "posts older than ID X" is exact (05.07 Keyset pagination). The same post arriving by push and by pull is removed in the merge.
export type FeedPost = { id: bigint; author: string }; // Snowflake IDs: bigger is newer
export function feedPage(pushed: FeedPost[], pulled: FeedPost[][], limit = 20, before?: bigint) {
const seen = new Set<bigint>();
const merged = [...pushed, ...pulled.flat()]
.filter((p) => (before === undefined || p.id < before) && !seen.has(p.id) && seen.add(p.id))
.sort((a, b) => (b.id > a.id ? 1 : b.id < a.id ? -1 : 0))
.slice(0, limit);
const next = merged.length === limit ? merged[merged.length - 1].id : undefined;
return { posts: merged, next }; // the client sends `next` back as `before`
}Tested with 60 posts, a third of them from a pulled celebrity and one of those also pushed (the author had just crossed the threshold): paging 20 at a time returned all 60 posts across the pages, each exactly once, strictly newest first, even though five new posts arrived after the first page. The new posts weren't shown in that scroll; they appear at the top on the next refresh.
Follows, unfollows and deletes. A new follow backfills the author's last few posts into the follower's feed in the background. An unfollow doesn't rewrite the feed: the read path filters out authors the user no longer follows, and the entries age out. A deleted post is filtered during hydration.
Ranking. If the feed isn't chronological, gather a few hundred candidates the same way, score them with a model, and sort. Fan-out gathers; ranking orders. Cursors then point into a ranked list saved for that session.
6. Wrap-up
What breaks first: fan-out lag when many large-but-not-huge authors post at once, so fan-out workers get their own autoscaling queue and the threshold is tuned by measuring; then the feed cache's memory as active users grow, handled by capping lengths and evicting inactive users' feeds.
What's expected at each level
Junior
gets a working design
- Stores posts and follows; builds the feed by querying followed authors
Mid-level
explains the trade-offs
- Precomputes feeds by fan-out on write and explains the celebrity problem
- Stores IDs and hydrates from a cache
- Uses cursor pagination
Senior
drives it and goes deep
- Uses a hybrid with a threshold and skips inactive users
- Handles follow, unfollow and delete without rewriting feeds
- Places ranking after candidate generation and sizes the feed cache
Seen in production
Yahoo! Research
2010Feeding Frenzy
The paper defines push as writing events into materialized per-consumer feeds, and pull as fetching from per-producer event stores at query time. It notes that fan-outs and update rates are highly skewed, and shows the global cost is minimised by deciding for each producer-consumer pair based on the ratio of the producer's update rate to the consumer's query rate; on Yahoo's PNUTS, this hybrid gave the lowest system load.
What we foundDecide push or pull for each producer-consumer pair from their relative event and query rates; the hybrid gives the lowest load.
Feeding Frenzy: Selectively Materializing Users' Event Feeds (SIGMOD 2010)jeffterrace.com ↗TAO
TAO serves the social graph with a simple data model of objects and associations, implemented over sharded MySQL with a geographically distributed, graph-aware cache. The paper reports that TAO processes a billion reads and millions of writes each second, and that in a sample of requests 99.8% were reads.
What we foundThe graph a feed is built from: objects and associations on sharded MySQL behind a cache; 99.8% of requests are reads.
TAO: Facebook's Distributed Data Store for the Social Graph (USENIX ATC 2013)usenix.org ↗Snip Bio's feed uses ?page=2 with OFFSET 20. Users report seeing the same posts again while scrolling. What's happening?
Recap
Interview questions
Design a news feed like Facebook's or X's home timeline.