Practice: FB News Feed
Design a feed from posts and follow relationships with hybrid fanout, ranking, stable pagination, privacy changes, and deletion.
Design a feed from posts and follow relationships with hybrid fanout, ranking, stable pagination, privacy changes, and deletion.
Write your own design before opening hints or the solution review. The numbers below are exercise assumptions, not claims about any company’s production traffic. You may challenge an assumption, but record the replacement and explain which decision changes.
Scenario and constraints
- 10 million daily readers; highly skewed follower counts.
- Ordinary author: 500 followers and two posts/day.
- Largest author: 20 million followers and ten posts/day.
- Privacy revocation must be enforced before content is returned.
Your deliverables
- Calculate write fanout for ordinary and largest authors.
- Separate authoritative posts/permissions from feed candidate projections.
- Draw merge, dedupe, visibility checks, ranking, and cursor pagination.
- Explain rebuild, delayed fanout, and deletion propagation.
Reason about this sequence
Figure — Private post enters feed cache → Viewer loses access → Projection removal is delayed → Read checks current visibility → Post filtered before response
For every step, annotate what is durable, what the caller knows, and which identity survives a retry. Identify the point where two concurrent actors could disagree. Do not assume a timeout means failure or a cache value grants ownership.
Interviewer follow-ups
- A stale feed cache contains a newly private post.
- A celebrity post causes a massive fanout backlog.
- A retry inserts the same candidate twice.
Answer each follow-up using the same design first. If it breaks, change the smallest boundary that repairs the invariant and explain the new cost. Show whether the change adds latency, storage, coordination, or operational work.
Staged hints
Hint 1 — reveal
Answer: Hint 1 — Treat every precomputed feed entry as a candidate. The final read must still establish that this viewer can see this post now.
Hint 2 — reveal
Answer: Hint 2 — For ordinary authors, fanout-on-write inserts candidate IDs into follower inboxes. This makes reads cheap but multiplies writes. For authors with huge audiences, retrieve recent posts at read time to avoid enormous synchronous fanout. The threshold depends on follower count, posting rate, and follower activity, not a universal celebrity number.
Hint 3 — reveal
Answer: Hint 3 — A private post remains in a stale feed cache after the viewer is removed from the audience. If the renderer trusts the cached entry, privacy is violated. Recheck current visibility at an appropriate authority or use a provably current authorization mechanism before returning content. Asynchronous deletion cleanup alone is not sufficient for an immediate revocation requirement.
Evidence-based self-review
- Hybrid fanout is justified with workload arithmetic.
- Cached candidates do not grant permission to read content.
- Stable identities and cursors handle retries and pagination.
- Projection repair and operational backlog limits are clear.
Score each item 0 if absent, 1 if named without an enforceable mechanism, or 2 if the mechanism and a failure are explained. Record evidence from your own diagram beside the score. Then choose one weak decision, revise it, and repeat the relevant follow-up. This rubric is a learning tool, not a hiring forecast.
Reveal the full worked solution
Save your attempt before continuing. ## Interview scope and guarantees
Publish posts, follow authors, read a ranked feed, and remove or restrict posts. Separate candidate freshness from authorization: a stale feed may omit a new post, but must not reveal a now-private post. Start with text and media references; media transfer is a separate path.
Capacity worksheet
Assume 10 million daily users open the feed 20 times: 200 million reads/day, about 2,315/s average and 11,574/s at 5× peak. One million posts/day with 200 followers creates 200 million inbox inserts/day. One author with 20 million followers can exceed that daily average in one burst.
Concrete API contract
POST /posts {text,mediaIds,visibility} -> {postId}
POST /follows {authorId}
GET /feed?cursor=... -> {items,nextCursor}
DELETE /posts/{id} -> 202Data model and access paths
posts(post_id PK,author_id,created_at,visibility,version,deleted_at)
follows(follower_id,author_id); UNIQUE(follower_id,author_id); INDEX(author_id,follower_id)
inbox(user_id,sort_key,post_id,source_version); PK(user_id,sort_key); UNIQUE(user_id,post_id)
fanout_tasks(post_id,shard,cursor,state); PK(post_id,shard)
feed_session(session_id,viewer_id,candidate_ids,rank_version,expires_at)Evolve a solution and explain each change
Figure — Three architecture decisions for FB News Feed, including the pressure each introduces.
Step 1: Read on demand
Fetch recent posts from followed accounts and merge by stable ordering. A user following many accounts requires excessive read work.
Step 2: Fan out ordinary authors
Populate follower candidate lists asynchronously when a post is published. A celebrity creates a huge write burst; projections may lag.
Step 3: Use a hybrid feed
Merge cached candidates with high-fanout authors at read time and recheck visibility. Ranking, pagination, and revocation still need defined semantics.
Responsibility overview
Figure — Connected responsibilities for FB News Feed. Trace the authoritative and derived paths separately.
Worked end-to-end scenario
An ordinary author publishes p10. The post transaction stores content and an outbox entry; workers add its ID to follower candidate lists. A celebrity publishes p11, which stays in an author timeline and is merged at read time. A reader page merges and deduplicates these sources, applies visibility, and ranks a bounded candidate set. If a worker replays p10, the candidate key prevents a duplicate. Later the author removes a follower. The old candidate list may retain p10, so access must be checked when content is assembled. A cached candidate is never proof of current permission.
Why these access paths matter
Posts are indexed by author, creation order, and ID. Candidate lists are keyed by recipient and ordered post identity. Follow relationships need both follower and followee access paths for reads and fanout. Use a cursor tied to a ranking snapshot or explicitly tolerate live-feed movement; an offset across a changing ranked list can duplicate or skip items.
Build the baseline first
- Author → post store → publication event.
- Feed reader → followed authors → recent posts.
- Authorization filter → ranking → response.
Evolve the design under load
- Ordinary author → fanout workers → follower inboxes.
- High-fanout author → author timeline → read-time merge.
- Candidate merge → bounded ranker → stable page cursor.
Defend the hardest decision
Choose the push/pull threshold from fanout cost and observed reader activity, not a fixed celebrity label. Pushing to inactive users wastes writes; pulling every author for every read wastes latency. Merge both paths using post ID deduplication. Ranking a bounded candidate set is affordable, but a changing score can cause duplicates or gaps across pages. A session snapshot or stable cursor semantics makes that behavior explicit.
Failure and recovery analysis
Fanout workers fall behind while a celebrity publishes. Keep publication durable, protect read latency, and surface a freshness metric. Deletion events remove projections eventually, but the final response checks current visibility or an authoritative revocation layer. On follow changes, decide whether the next page uses the existing snapshot or starts a new feed session.
Security and privacy boundary
Apply block lists, visibility changes, and tenant boundaries after candidate retrieval. Avoid retaining deleted post text in secondary indexes indefinitely.
Interview follow-ups with reasoning
Question: What if the ranking service fails?
Show answer and explanation
Answer: Serve a chronological fallback from authorized candidates.
Question: How do you rebuild inboxes?
Show answer and explanation
Answer: Replay source events into a new projection generation and compare coverage before switching.
Question: How do you avoid expensive fanout retries?
Show answer and explanation
Answer: Checkpoint follower ranges and make (user,post) insertion idempotent.
Operate and verify the design
Feed p99, fanout age, ranking fallback rate and visibility-revocation delay.
Publish a celebrity post during fanout backlog and revoke an older post while readers paginate.
A second scenario to test transfer
An ordinary author has 500 followers and posts twice a day: precomputation creates about 1,000 candidate writes/day. A celebrity has 20 million followers and posts ten times: naive fanout creates 200 million candidate writes/day. Pulling that author’s recent posts during active readers’ requests can be cheaper, especially when many followers are inactive. Explain the resulting read merge cost.
How do you prevent an old feed entry from exposing a post after access is revoked?
Show answer and explanation
Answer: Treat the feed entry as a candidate and validate current visibility before returning content. Also propagate removal events to reduce stale work, but do not rely solely on eventual cache cleanup when the contract requires immediate revocation.
Compare alternatives
| Strategy | Strength | Cost |
|---|---|---|
| Fanout-on-write | Cheap common reads | Amplified writes |
| Fanout-on-read | Cheap author writes | Read-time merge |
| Hybrid | Handles audience skew | Two paths and threshold policy |
| Visibility recheck | Protects stale candidates | Extra read-path work |
A design-changing exercise
Deleting a candidate entry fails after a privacy change. Can the post still appear?
Show answer and explanation
Answer: The serving path must enforce current visibility or a defined bounded permission policy. Projection deletion alone is insufficient protection.
Design workshop: choose hybrid fanout with a calculation
Core flows are publish, follow, fetch a ranked page and revoke visibility. Ads and ML model training are outside the baseline. Assume feed p95 under 200 ms, ordinary post projection within five seconds and current authorization before returning protected content. A missing fresh candidate is acceptable within that bound; a private post exposed to an unauthorized reader is not.
An author with F followers, active-reader fraction a, P posts/day and R feed reads/day per active follower costs roughly F×P candidate writes under push versus a×F×R author-timeline merges under pull. The costs are different operations, so compare measured write and merge cost, not only their counts. With F=20 million, P=10, a=0.05 and R=5, push is 200 million candidate writes/day while pull involves five million active feed-read merges. This motivates pull for that author; thousands of ordinary authors can still use push.
# A bounded candidate merge, before expensive ranking.
pushed = inbox_candidates(viewer, cursor)
pulled = merge_recent_timelines(celebrity_follows(viewer))
candidates = unique_by_post_id(pushed + pulled)
authorized = batch_filter_current_visibility(viewer, candidates)
ranked = rank(authorized, pinned_rank_version)
return freeze_session(ranked[:500], viewer, ttl_minutes=5)A session stores viewer identity, ordered candidate IDs and rank version. The cursor is signed session ID plus offset; it is not an unconstrained timestamp. Pagination advances through the frozen list while rechecking current visibility, topping up from later candidates if privacy removals shorten a page. Expiry returns a reset response. New posts appear on refresh rather than shifting an existing session's page boundaries.
Figure — Push ordinary authors and pull celebrities.
The follower index needs both follower→authors and author→followers access paths. Shard fanout_tasks by post and follower-range; insert a batch and its checkpoint in one projection transaction. Retried batches use unique(user_id,post_id), not a newly generated sort key. Source versions reject stale deletion/republication events. If rank placement changes, update the candidate row rather than create another post identity.
Figure — Recovering a feed without leaking revoked posts.
Hot fanout authors use the pull path, but a reader following many celebrities can still create unbounded merges. Cap the number of timelines queried per request, cache eligible public author slices, fetch recent windows and define graceful freshness degradation. Operate on candidate IDs first; media and body hydration happen after authorization. Measure fanout lag, page fill ratio, stale-version rejection, ACL denials and celebrity merge time separately.
Exercise: p17 is fanout-delivered twice with two event IDs, then deleted, then an old publish event is replayed. How many visible copies should remain?
Show answer and explanation
Answer: Zero after deletion becomes authoritative. Candidate uniqueness handles duplicate insertion; a retained post version/tombstone rejects the old publish. The final ACL/deletion check protects the reader even before all projection cleanup completes.
Technical references
How do you prevent an old feed entry from exposing a post after access is revoked?
Your design draft
Clarify assumptions, explain your approach, and test the difficult cases. Save your draft, then compare it with the study notes.
Self-review checklist
Self-guided practice. Automated AI feedback and code execution are not connected.