Precompute feeds by fan-out on write for ordinary accounts: each new post id is pushed into every follower's feed list. Skip that for accounts with millions of followers and pull their recent posts at read time. On a read, merge the candidate sets, rank them and return the top page. The hybrid keeps reads fast and celebrity posts cheap.
hardPattern: CachingFrom Devana's Meta question set
Problem
Design a social network's news feed end to end: how candidate posts are gathered, how they are ranked, how feeds are delivered and how they are stored.
Pay particular attention to the difference between a user with 200 friends and a page with 50 million followers.
POST /posts {author_id, content} -> {post_id}
GET /feed?cursor=... -> {posts: [...], next_cursor}This is one of Devana's own questions, written in the style of Meta's interviews. It is not a question leaked from Meta, and Devana is not affiliated with Meta.
Requirements
- 3 × 10^9 users and 10^6 feed requests per second.
- A feed loads in under a second.
- New posts from close friends appear within seconds.
- A single post may need to reach 50 million followers.
- Worth confirming first: the feed is ranked rather than chronological, so a stored feed is a list of candidates, not the final order.
Scenarios
Scenario 1
A user with 200 friends posts. Expected: The post is in those friends' feeds within seconds.
Fan-out on write: 200 small appends to feed lists, done by workers reading from a queue.
Scenario 2
A page with 50 million followers posts. Expected: No 50 million writes; followers see the post on their next refresh.
The page is marked high-fanout, so its posts are only written to its own timeline. Each follower's feed request pulls the recent posts of the few high-fanout accounts they follow and merges them in.
Hints
Open one at a time, and try the problem again after each.
Hint 1
Compare the extremes: build every feed at read time, or precompute every feed at write time. Where does each one break?
Hint 2
What does one post from an account with 50 million followers cost under fan-out on write?
Hint 3
Most users follow only a handful of huge accounts. Could you pull just those at read time?
Hint 4
Ranking needs features about the post and the viewer. How many candidates can you afford to rank per request?
A worked answer
Requirements and estimates
10^6 feed reads a second is the load to design for. If each read ranks 500 candidates, that is 5 × 10^8 scorings a second, so candidate lists must be small and mostly precomputed. Posts are written far less often than feeds are read.
Fan-out on write or on read
Fan-out on read builds the feed at request time from everyone the user follows: writes are cheap and reads are expensive, and a user following 2,000 accounts makes every refresh slow.
Fan-out on write appends each new post id to every follower's precomputed feed list: a read becomes one lookup, but one post from a 50-million-follower page is 50 million writes, and the backlog delays everyone else's posts.
The hybrid
Push for normal accounts and pull for high-fanout ones, marked by a follower threshold. A post from a normal account goes on a queue, and workers append its id to each follower's feed list in a cache, capped at a few hundred ids. A post from a high-fanout account is written only to that account's own timeline.
On read, the feed service takes the user's precomputed list, adds the recent posts of the high-fanout accounts the user follows, and ranks the merged candidates.
Ranking
Rank in two stages: a cheap model narrows the candidates to a few hundred, then a heavier model scores those on predicted engagement, from features of the post, the author and the viewer's history.
Cache the ranked result briefly. A user refreshing five times a minute should not trigger five full rankings; score only the candidates that arrived since and merge them into the cached order.
Storage
Posts live in a store sharded by post id. Feed lists hold ids only and are capped, so they do not grow without bound; older history is rebuilt on demand by pulling. The follow graph is its own sharded store, read heavily by the fan-out workers.
How to say it in the interview
Name both fan-out strategies and how each one fails before choosing, then introduce the hybrid as the answer to the 50-million-follower case the question points at. Put a number on a celebrity post under pure push. When you reach ranking, say how many candidates you rank and why caching the ranked feed matters for frequent refreshes.
The core in code
The read-time merge in code: the user's precomputed feed (pushed post ids) and the recent timelines of the high-fanout accounts they follow, each newest first, merged into one candidate list for ranking without sorting everything.
Python
import heapq
def merge_candidates(pushed, pulled_timelines, limit):
"""Each list holds (timestamp, post_id), newest first. Returns up to
limit post ids, newest first, without duplicates."""
streams = [pushed, *pulled_timelines]
# The heap holds the newest unread entry of each stream.
heap = [(-s[0][0], s[0][1], i, 0) for i, s in enumerate(streams) if s]
heapq.heapify(heap)
out, seen = [], set()
while heap and len(out) < limit:
_, post_id, i, j = heapq.heappop(heap)
if post_id not in seen: # a post can arrive by both paths
seen.add(post_id)
out.append(post_id)
if j + 1 < len(streams[i]):
ts, nxt = streams[i][j + 1]
heapq.heappush(heap, (-ts, nxt, i, j + 1))
return outJavaScript
function mergeCandidates(pushed, pulledTimelines, limit) {
// Each list holds [timestamp, postId], newest first.
const streams = [pushed, ...pulledTimelines];
const next = streams.map(() => 0); // index of each stream's newest unread entry
const out = [];
const seen = new Set();
while (out.length < limit) {
// The newest head across the streams; ties go to the smaller post id.
let best = -1;
for (let i = 0; i < streams.length; i++) {
if (next[i] >= streams[i].length) continue;
if (best === -1) {
best = i;
continue;
}
const [ts, id] = streams[i][next[i]];
const [bestTs, bestId] = streams[best][next[best]];
if (ts > bestTs || (ts === bestTs && id < bestId)) best = i;
}
if (best === -1) break;
const [, postId] = streams[best][next[best]++];
if (!seen.has(postId)) { // a post can arrive by both paths
seen.add(postId);
out.push(postId);
}
}
return out;
}Both versions are tested against the same cases, including every example on this page.
- The merge reads only as far as
limit, so following many high-fanout accounts does not mean reading all of their history. - Duplicates are expected: an account can cross the high-fanout threshold while its earlier posts are still in followers' pushed lists.
- The JavaScript version scans the stream heads instead of using a heap, which is just as fast for the few dozen streams a user has; with hundreds, use a heap.
Follow-up questions
Fan-out on write or on read? Where exactly do you switch between them?
Per author, at the follower count where pushing one post costs more than pulling that author's timeline on every follower's read. Measure both costs to set it, and add hysteresis so an account near the line does not flip back and forth.
A celebrity posts. Walk me through what happens.
The post is written once to the author's timeline and indexed; no fan-out job runs. Each follower's next feed request pulls that timeline as one of a few high-fanout sources, so the post appears on refresh and is ranked like any other candidate.
How do you avoid recomputing ranking for a user who refreshes five times a minute?
Cache the ranked feed for a short time, together with the candidate set it was computed from. On refresh, score only the candidates that arrived since and merge them in.
How do you store feeds so storage does not grow without bound?
Feed lists hold post ids only and are capped at a few hundred entries. Lists for inactive users are dropped and rebuilt by pulling when they come back.
Common mistakes
- Pure fan-out on write, then discovering the 50-million-follower post.
- Pure fan-out on read, which makes every refresh query hundreds of timelines.
- Storing whole posts in each feed list instead of ids.
- Ranking every candidate with the heaviest model.
Practice it in a mock interview
Reading a solution and explaining one under questioning are different skills. The button below starts this problem as a voice mock interview with Josh, Devana's AI interviewer, with a whiteboard he can read.
Practice this problem in a mock interview
Related problems
- Select the Top Unseen Feed Posts: Filter with a set lookup, then a bounded heap ordered by score and post id.
- Design Inventory for a Flash Sale: Admission control in front, atomic reservations with expiry, and orders confirmed behind a queue.
- Caching: the pattern guide, with more practice questions.