Prompt and Applicable Context
Design a personalized news feed backed by a follow graph. People can publish text or image posts, follow or unfollow authors, and browse a relevance-ranked home feed. Deleting a post, making it private, blocking an author, or unfollowing must affect subsequent reads promptly. Likes, comments, and recommendation models are ranking signals. Media transcoding, comment trees, ad auctions, and model training are out of scope.
Assume 50 million daily active users. Each active user reads 20 pages per day, with 20 items per page, and the system receives 10 million new posts per day. Feed-read p99 must be below 200 milliseconds. A normal author's post should become visible to active followers within five seconds at p99. A normal author averages 200 eligible followers, while the largest author has 50 million followers. These figures and deadlines are interview constraints, not claims about an existing product.
This prompt fits mid-to-senior backend, infrastructure, and system-design interviews. Public interview material asks directly about write fan-out, read fan-out, celebrity authors, cursor pagination, caching, and consistency. First-party engineering accounts also separate candidate retrieval, aggregation, filtering, and multi-pass ranking while retaining materialized content for graceful degradation. A strong answer therefore combines mechanisms from explicit cost and correctness requirements instead of reproducing one fixed diagram.
What the Interviewer Evaluates
First, can the candidate quantify the read-write asymmetry? Fifty million DAU times 20 pages is one billion reads per day, or about 11,600 average QPS. A five-times peak is about 58,000 QPS. Ten million posts average only about 116 creates per second, but write fan-out to 200 eligible followers expands that into about two billion timeline-reference inserts per day, or roughly 23,100 per second on average. Comparing only raw post QPS hides the main write load.
Second, does the candidate understand the boundaries of push, pull, and a hybrid? Pure fan-out-on-write makes reads cheap, but one author with 50 million followers creates 50 million inserts. Pure fan-out-on-read makes publishing cheap, but every read may merge many followed authors. A strong design precomputes references for normal authors, pulls high-fan-out authors during reads, and derives the boundary from active followers, posting rate, queue budget, and freshness goals.
Third, can the candidate separate the post source of truth, candidate pools, and final presentation? A timeline stores lightweight references rather than copied bodies. The aggregator merges a precomputed inbox, recent posts from high-fan-out authors, and recommendation candidates; applies authorization, deletion, blocking, and deduplication; then ranks and hydrates. A model score never overrides visibility rules.
Fourth, can the candidate preserve useful pagination semantics while ranking changes? Scores move with engagement and model versions, so an offset or score alone causes duplicates and omissions. The answer should propose either a short-lived frozen feed session or a versioned compound cursor and explain what happens when new posts, deletes, unfollows, and visibility changes occur during scrolling.
Finally, the design must recover and be falsifiable. The event bus, cache, and fan-out workers can fail or repeat work. Idempotent references, lag monitoring, bounded source fallback, tombstone filtering, and repair scans must converge. Validation should include celebrity bursts, duplicate and out-of-order events, unfollow races, deletion leaks, and cache loss.
Questions to Clarify Before Answering
- What feeds the home page? Primarily posts from followed authors, with a small recommendation pool. Ads, groups,
and topic feeds are out of scope.
- What is the ranking contract? Relevance ranking is the default. Results should be fresh and mix sources
deliberately, but strict chronological order is not required.
- Who may see a post? A post can be public, followers-only, or private. Blocks, deletes, and visibility restrictions
take precedence over cache hits and ranking.
- Who needs read-your-writes? The author should see a new post immediately. Active normal followers have a five-second
visibility target. Offline followers may be caught up on their next visit.
- How stable must pagination be? One continuous scroll should minimize duplicates and gaps. New posts may appear on
refresh or in a new session. Deletion and revoked access apply immediately within the current session.
- How skewed is the graph? A normal author averages 200 eligible followers, while the largest can have 50 million.
An average must not conceal hotspots.
- How much is retained? Precompute the newest 500 candidate references for each active user. Retain post bodies under
product policy and retrieve older items from author and recommendation indexes when needed.
- What are the regional requirements? Serve reads locally and replicate a post asynchronously after it commits in
the author's home region. Ordinary freshness may be seconds; visibility restriction uses a higher-priority invalidation path.
30-Second Answer Framework
“I would separate the authoritative post store, follow graph, and per-user candidate inbox. A create transaction writes the post and an outbox event. Fan-out workers idempotently insert normal authors into active-follower inboxes, while high-fan-out authors write only an author-recent index and are pulled during reads. The feed aggregator merges precomputed, high-fan-out, and recommended candidates; applies authorization, deletion, blocking, and deduplication; then uses retrieval and ranking to create a short-lived feed session. The cursor contains the session and position, and post bodies are hydrated only for the final items. Deletes and visibility restrictions write authoritative tombstones and high-priority invalidations, while every read rechecks them. During queue lag, return an older materialized feed and perform bounded source reads. Monitor post-to-visible latency, duplicates, deletion leaks, and each stage's p99.”
Step-by-Step Deep Dive
Step 1: Derive architecture budgets from capacity
Daily feed reads are:
50,000,000 DAU × 20 pages/day = 1,000,000,000 feed reads/day
1,000,000,000 / 86,400 ≈ 11,574 average read QPS
11,574 × 5 peak factor ≈ 57,870 peak read QPSPost creation averages 10,000,000 / 86,400 ≈ 116 QPS. If normal authors fan out to 200 active followers on average, they produce about two billion candidate-reference inserts per day, or 2,000,000,000 / 86,400 ≈ 23,148 inserts per second on average. One 50-million-follower post exceeds many seconds of the normal write budget, so synchronous full fan-out is unsafe.
Keeping 500 references for each of 50 million active users produces 25 billion references. If a rough reference uses 64 bytes for postid, authorid, an initial score, time, and flags, the logical lower bound is about 1.6 TB before indexes, engine overhead, and replicas. Timelines should therefore store bounded references, not copied post bodies, and use activity-tiered storage.
Step 2: Define ownership, APIs, and the minimum data model
The post service owns body, author, creation time, visibility, version, and deletion tombstone. The social-graph service owns following and followers adjacency indexes. The feed service owns candidate references, sessions, and impression records. Object storage and a CDN hold images; a timeline contains only media references. Core APIs can be:
POST /v1/posts create a post with Idempotency-Key
DELETE /v1/posts/{post_id} write a deletion tombstone
PUT /v1/users/{id}/following/{author} follow an author
DELETE /v1/users/{id}/following/{author} unfollow an author
GET /v1/feed?cursor=...&limit=20 read one ranked page
GET /v1/posts/{post_id} hydrate one visible postposts stores authoritative state by postid and a recent index on (authorid, createdat, postid). follows must list followees by viewer and shard followers by author. feedinbox partitions by viewerid; its sort key includes a retrieval score, creation time, and postid. feedsessions temporarily stores a ranking version, ordered candidate IDs, and expiration. Impression and engagement events go to separate logs for features and experiments rather than synchronously updating every candidate during a read.
Post creation takes a caller idempotency key. The post and outbox event commit in one database transaction, after which the author can read from the post source of truth. The bus provides at-least-once delivery. A unique or conditional (viewerid, postid) write converges duplicate fan-out. Events carry the post version so an old event cannot resurrect a deleted or newly restricted post.
Step 3: Use hybrid fan-out for normal and high-fan-out authors
After a normal author posts, the fan-out service reads shards of eligible active followers and batch-inserts a lightweight reference into each inbox. Workers track progress with shard cursors and event IDs. A timed-out batch retries the same work without creating a second candidate. Offline or long-inactive users need not be materialized immediately; their recent candidates can be rebuilt from the follow graph on their next visit.
A high-fan-out post goes only to the (authorid, createdat) recent index and a hot cache. When a viewer follows such authors, the aggregator pulls a bounded number of recent items from each in parallel and merges them with the inbox. The boundary should not be one permanent follower-count constant. Estimate eligibleactivefollowers × postsperwindow write cost and compare it with expected read-merge cost, queue headroom, and the five-second freshness budget. If an author becomes popular or posts in a burst, the control plane can switch the author to pull mode. Already queued batches may complete idempotently or be canceled, but both paths must not run without bounds.
Pull can still create expensive read fan-out when a viewer follows many high-fan-out authors. The aggregator limits candidates, concurrency, and deadlines per source; caches recent-author indexes; and returns other eligible candidates when one source times out. Regional or follow-cluster preaggregation is possible, but it is justified only after measured merge cost becomes a bottleneck.
Step 4: Separate candidate generation, filtering, ranking, and hydration
The read path has four stages:
- Read a candidate batch from the normal-author inbox and bounded batches from high-fan-out and recommendation sources.
- Deduplicate by
post_id, then check follows, blocks, tombstones, current visibility, and regional policy. - Use a cheap score to reduce the set, then a heavier model for engagement likelihood, freshness, source quality, and
negative feedback, followed by diversity and frequency constraints.
- Batch-hydrate body, author summary, and aggregate counts for the final 20 items, return them, and log impressions
asynchronously.
Meta's published engineering account describes a feed aggregator that collects candidates, objects, and features before ranking, with multiple model passes that reduce computation. Pinterest's published architecture likewise separates unseen candidate pools, content generation, and a materialized feed. An interview design need not copy either system, but these accounts show why copying full posts into a cache and sorting there lacks important boundaries.
Authorization and product constraints apply before and after ranking. Early filtering saves work; rechecking the post version during hydration blocks a delete or visibility restriction that arrives during ranking. A model score is only an ordering input and cannot reintroduce invisible content. Engagement counts may be eventually consistent. Author, body, visibility, and tombstone come from the authoritative post version.
Step 5: Stabilize dynamic ranking with a feed session
An offset repeats or skips items when new content arrives or scores change. A (score, post_id) keyset alone is also insufficient when reranking changes the score before the next page. The first request can rank up to 500 candidate IDs and store them under a 30-minute feedsessionid. An opaque cursor contains the session, next position, query fingerprint, and signature. Later pages read by position, recheck visibility, and fill holes.
New posts appear when the viewer refreshes or starts a new session rather than being inserted into the middle. Deletes, blocks, and visibility restrictions filter immediately, so a current session may lose an item; the aggregator fills from later session candidates. If the session cache is lost or expires, return a recognizable cursor-expired result so the client can keep already displayed IDs and refresh. Do not silently apply an old position to a new ranking.
If storing 500 frozen IDs is too expensive, store a ranking epoch and compound keyset and let the client submit a summary of displayed IDs. That design uses less session storage but makes deduplication, model changes, and deletion fill harder. Choose from continuous-scroll duration, acceptable duplicate rate, and session budget rather than treating cursor encoding as the main answer.
Step 6: Converge follows, deletes, and visibility changes correctly
After a follow commits, the viewer's next read can pull the new author's recent posts while a bounded backfill runs asynchronously. Unfollow and block first update the authoritative graph; the read path immediately filters that author, then cleans the inbox asynchronously. Millions of physical deletes do not have to finish before presentation stops. If follow and post events arrive out of order, a relationship version in the candidate is only an optimization. Final visibility still uses the current authoritative rule.
When deleting a post or making it private, the post service commits the new version, tombstone, and high-priority outbox event together. Cache invalidation, search cleanup, and inbox cleanup may be asynchronous, but feed hydration batch-reads the current version and filters it. A hot tombstone cache shortens checks, while the durable post record prevents stale content from reappearing after cache loss. A repair scan removes old references; physical cleanup is not authorization.
Likes and comments should not rewrite every follower inbox whenever a score changes. Engagement events update aggregate counts and features, and active viewers rerank candidates on their next request. Extremely hot content can update a shared feature cache. This accepts temporary ranking staleness and avoids turning each interaction into another global fan-out.
Step 7: Design degradation, recovery, and regional boundaries
During fan-out queue lag, protect post creation and materialized-feed reads first. The feed can return an older eligible candidate set and perform bounded source reads for the viewer's recently active followees. Freshness may degrade; authorization may not. If one author creates a queue hotspot, switch that author to pull mode and cap unfinished batches. After recovery, catch up idempotently by event time and version. Monitor oldest event age, not only queue length.
If the candidate cache fails, read fewer candidates from the durable inbox or return a stored session. If ranking times out, use a deterministic freshness score plus diversity rules. When the authoritative post or graph store is unavailable, serve only previously verified content within a safe authorization TTL. After that TTL, fail or narrow results rather than displaying content whose access may have been revoked.
In multiple regions, the author's home region accepts a post and assigns a global ID, then events replicate to read regions asynchronously. Ordinary content may have second-level freshness; the author gets immediate read-your-writes through the home region. Deletes, blocks, and visibility restrictions use higher-priority replication, and old replicas are restricted until the global tombstone service confirms state. Active-active follow-graph writes add relationship conflicts and should be introduced only for an explicit requirement.
Step 8: Prove the boundaries with metrics and fault injection
Core metrics include create success, post-to-first and post-to-95-percent-active-follower visibility, per-shard fan-out throughput and oldest lag, high-fan-out pull latency, candidate count, deduplication rate, filter rate, retrieval and ranking p99, hydration batch size, cache hit rate, cursor expiry, page duplicates, empty pages, and leakage of deleted or revoked content. The last metric must remain zero and needs active synthetic probes.
Fault injection should cover duplicate and out-of-order post events; repeated posts by a 50-million-follower author; an author switching from push to pull mid-fan-out; concurrent follow, unfollow, and post; delete or privacy change while paging; lost tombstone cache; one slow follower shard; event-bus outage and catch-up; ranking timeout; lost feed-session node; and regional replication delay. Every scenario asserts that no unauthorized content appears, references converge, latency degradation is observable, and recovery does not amplify duplicate writes.
High-Quality Sample Answer
“With 50 million DAU and 20 pages each day, I get about 11,600 average feed reads per second and 58,000 at a five-times peak. Ten million daily posts are only about 116 QPS, but 200 active followers per normal author expand that to about 23,100 candidate inserts per second on average. One post to 50 million followers cannot use synchronous full push.
I would separate the post source of truth, social graph, and candidate inbox. The create transaction writes the post and outbox. Normal-author events fan out by follower shard into per-user inboxes with viewerid + postid idempotency. High-fan-out authors write only a recent-author index and are pulled and merged during feed reads. The push-pull boundary uses active followers, posting rate, queue budget, and read cost. Timelines store references; bodies and media are batch-hydrated only for final items.
For a read, the aggregator obtains bounded candidates from inbox, high-fan-out, and recommendation sources. It filters and deduplicates by current follows, blocks, tombstones, and visibility before cheap retrieval, heavier ranking, diversity, and frequency control. The first page freezes up to 500 candidates in a 30-minute feed session, and the cursor contains session plus position. New posts wait for refresh, while deletes and visibility restrictions are filtered and backfilled on every page.
Delete and privacy changes commit an authoritative version plus a high-priority invalidation. Cache and inbox cleanup may lag, but every presentation rechecks state. During queue lag, return an older materialized feed and perform bounded source reads; during ranking timeout, fall back to freshness. Authorization never degrades. I would monitor post-to-visible latency, oldest lag, each ranking stage's p99, duplicate rate, and revoked-content leakage, then inject celebrity bursts, duplicate events, unfollow races, deletes, cache loss, and regional faults.”
Common Mistakes
- Comparing only raw read and write QPS. Posting averages about 116 QPS, but follower fan-out dominates writes.
Calculate amplification and the heavy-tailed graph.
- Using fan-out-on-write for every author. A 50-million-follower author causes 50 million inserts. Pull high-fan-out
authors during reads.
- Making the celebrity threshold a permanent constant. Follower activity, posting rate, and queue headroom move.
Classify from cost and SLOs and protect transitions.
- Copying full bodies into timelines. Edits, deletes, and visibility changes create a huge invalidation surface.
Store references and recheck versions during hydration.
- Using
offsetfor dynamically ranked pages. Inserts and reranking cause duplicates and gaps. Use a short-lived
feed session or a versioned compound cursor.
- Filtering unfollows and deletes only in background jobs. Cleanup lag leaks content. Check the authoritative graph,
tombstone, and visibility during reads.
- Claiming end-to-end exactly-once from the event bus. Workers, storage, and retries still duplicate. Converge with
event IDs, post versions, and unique candidate keys.
- Skipping authorization during degradation. Stale ordering and fewer candidates may be acceptable; unauthorized
display is not. Give safety filtering its own budget and failure policy.
- Testing only normal traffic. The important risks are high-fan-out authors, lag, reordering, and authorization races.
Fault injection must cover those boundaries.
Follow-Up Questions and Responses
Follow-up 1: How do both paths avoid duplicates when a normal author suddenly gains 50 million followers?
The control plane switches the author to pull mode under a version. Fan-out jobs read that mode version and allow only a bounded set of already claimed batches to finish. Candidates are idempotent on (viewerid, postid), so pushed and pulled copies merge into one. The queue gives each author a quota, and new posts during the transition go directly to the high-fan-out index.
Follow-up 2: What happens to an old feed session after the viewer unfollows an author?
A session freezes ordering, not authorization. Every page hydration batch checks the current follow and block graph. Unfollowed content is filtered and replaced from later candidates. Asynchronous inbox cleanup saves space but does not provide immediate authorization semantics.
Follow-up 3: How would you support strict chronological ordering?
Reuse the candidate sources and visibility filters, change the sort key to (createdat, postid), and use keyset pagination with an upper-bound snapshot time. New posts appear after refresh. High-fan-out and normal inbox sources still need a merge, but the heavier ranking session is unnecessary.
Follow-up 4: How do you isolate a duplicate-rate increase after a ranking-model release?
Compare pre- and post-dedup candidate counts, session-generation IDs, cursor positions, and impression records by model version. Determine whether duplication enters through a source, rank output, or lost session. Map source aliases to the authoritative post_id. If session storage is flapping, rolling back the model will not help; restore the session store and make clients refresh explicitly.
Follow-up 5: How do you know the hybrid fan-out boundary is correct?
Replay the real follower-count and activity distribution offline. Estimate each author's precompute writes, queue wait, read merging, and cache-hit cost. Move the boundary gradually online while observing post-to-visible p99, feed-read p99, total storage writes, and degradation rate. Add hysteresis so authors do not flap between push and pull modes.