Design Facebook News Feed: hybrid fan-out, ranking and privacy-safe reads
Build an illustrative social feed from durable posts and graph relationships. Explain candidate retrieval, ranking deadlines, celebrity skew, stable pagination and why cached feed entries are never authorization.
1. Ask what belongs in the feed
I would ask whether you want a chronological following feed or a ranked feed that also recommends unfamiliar content. I will design a ranked feed from friends and followed pages first. Ads and broad recommendations can be extensions because they introduce separate eligibility, budget and ranking concerns.
| Candidate asks | Illustrative interviewer reply | Design consequence |
|---|---|---|
| Which sources are included? | Friends and followed pages; groups and ads can wait. | Candidate retrieval starts from a bounded social graph and author timelines. |
| How is it ordered? | Personal relevance, with chronological fallback. | Separate candidate retrieval from a ranking service with a deadline. |
| What traffic should we assume? | 100 million daily active users, five feed requests each, 20 million posts/day. | Capacity ledger separates reads, posts and fan-out amplification. |
| How quickly should a post appear? | Normally within five seconds for active followers. | Measure fan-out freshness; authors see their own committed post immediately through an overlay. |
| Are relationships and posts private? | Yes; unfollow, block and deletion must affect future reads. | Eligibility is rechecked during hydration; cached candidate IDs are not permission. |
| Are celebrity accounts common? | A small number have millions of followers. | Hybrid push/pull avoids materializing every celebrity post to every follower. |
| How should pagination behave? | No duplicates within a scroll session; refresh can show new posts. | Use a short-lived ranked snapshot/cursor instead of offsetting a constantly changing score list. |
2. State the consistency boundary for each operation
I can tolerate a slightly stale ranking score but not treating an old audience decision as current authorization. I also distinguish a feed snapshot from a permanent promise: deleting a post can remove it from later pages even within an existing scroll session.
| Requirement | Agreed target or scope | Design implication |
|---|---|---|
| Publishing | Create a post after media is verified; acknowledge durable metadata. | Committed post and fan-out event share a transaction; media upload completion alone is not publication. |
| Reading | Illustrative p99 feed API under 500 ms, excluding full media download. | Bound retrieval, hydration and ranking; render media through a CDN separately. |
| Freshness | Active-follower visibility normally within five seconds under admitted load. | Author overlay and bounded pull fallback help during fan-out lag, but do not imply zero lag. |
| Privacy | Check current post status, block and audience rules before returning content. | If required authorization state is unavailable, omit affected content rather than use a stale allow decision. |
| Pagination | Stable ordering within a short session, unique post IDs per session. | Snapshot expires explicitly; deleted items may create gaps that bounded refill can fill. |
| Scope | Friends/followed pages and basic engagement features; no ad auction or model training platform. | Focus on serving correctness before expanding recommendation infrastructure. |
3. Quantify fan-out rather than just post writes
For this example I use 100M DAU, five feed requests per user per day and 20M posts/day. These are interview assumptions, not Facebook traffic figures. A small post-write QPS can still produce a very large feed-write workload.
| Quantity | Calculation | Interpretation |
|---|---|---|
| Feed reads | 100M × 5 / 86,400 = 5,787 requests/s average; 57,870/s at a 10x peak. | Rank/hydrate cost per request multiplies this load. |
| Post writes | 20M / 86,400 = 231/s average; 2,315/s at 10x. | Metadata writes are far smaller than candidate fan-out operations. |
| Naive fan-out | 20M posts/day × 200 eligible recipients = 4B candidate inserts/day. | 46,296 inserts/s average; the mean hides celebrity tails. |
| Post metadata | 20M × 1 KB × 30 days = 600 GB logical. | Media, older retention, indexes and replicas are separate budgets. |
| Active feed cache | 100M users × 500 candidate references × 24 bytes = 1.2 TB logical. | Three copies = 3.6 TB before data-structure overhead; cache fewer inactive users. |
| Hydration and ranking | 100 candidates/request × 5,787/s = 578,700 candidate evaluations/s average. | Batch reads and scoring; at 10x peak about 5.79M evaluations/s requires measured capacity. |
| Scroll snapshots | 1M concurrent sessions × 200 IDs/scores × 24 bytes = 4.8 GB logical. | Assumed concurrency is independent of DAU; cap snapshot lifetime and size. |
4. Keep posts authoritative and feeds disposable
I store a post once and put references in feed indexes. Author timelines and social-graph edges let me reconstruct candidates if a materialized feed is lost. Privacy decisions consult authoritative versioned state; asynchronous fan-out is an optimization, not the source of truth.
| Record and key | Fields / invariant | Access pattern |
|---|---|---|
| Post(postId) | authorId, mediaRefs, audience, createdAt, version, status. | Primary lookup and versioned delete; publish only verified media references. |
| AuthorTimeline(authorId, createdAt, postId) | Published post references in stable time order. | Pull high-fan-out authors and reconstruct missing feeds. |
| GraphEdge(viewerId, authorId) | friend/follow/block state, version and timestamps. | Eligibility check and paginated follower enumeration; reverse index supports fan-out. |
| FeedCandidate(viewerId, postId) | source, creation time and cheap preliminary score. | Idempotent insert; bounded retention and size. Not an ACL grant. |
| FeedSession(viewerId, sessionId) | ordered candidate IDs/scores, modelVersion, expiry and cursor position. | Stable short-lived pagination, bound to the authenticated viewer. |
| Engagement(eventId) | viewer, post, action, event time and experiment assignment. | Deduplicate events; train/evaluate separately from the synchronous serving path. |
5. Define publish, feed and deletion contracts
I use opaque cursors tied to the viewer and session. A client cannot change a cursor to fetch another user’s candidates. For first-page refresh I create a new ranking snapshot; for continuation I preserve the prior ordering while rechecking eligibility.
| Operation | Contract | Failure or retry behavior |
|---|---|---|
| POST /posts | Idempotency-Key, body, verified media references and audience. Return postId after commit. | Reject media not owned/ready; retry returns the same post for the same payload. |
| GET /feed?cursor=...&limit=20 | Returns authorized hydrated posts, nextCursor and session expiry. | Expired cursor yields an explicit refresh response; page size and work are bounded. |
| PUT /relationships/{authorId} | Version-aware follow/unfollow/block change. | Authorization applies immediately at the chosen read authority; feed cleanup can be asynchronous. |
| DELETE /posts/{postId} | Owner-authorized tombstone/version update. | Idempotent deletion; future hydration omits the post even if feed indexes still reference it. |
| POST /engagements | Event ID, action and post reference. | Authenticate and validate; replayed telemetry does not create duplicate logical events or confer content access. |
6. Draw candidate generation before ranking
I keep candidate retrieval, eligibility, hydration and scoring explicit. Combining them into a single magic recommendation box hides latency and privacy decisions. A feed-cache entry can help find a post but cannot authorize returning its text or media.
7. Trace a post and a retryable fan-out
Suppose a regular author posts a photo. I first verify the media is ready and owned by the author. Then I commit the post and publication intent. A fan-out worker enumerates eligible active followers in pages and inserts post references idempotently, so a crash halfway through a page can be retried.
Commit publication
Persist post, author timeline update or its projection intent, and publication event atomically. The author response includes the committed post even before follower caches catch up.
Choose fan-out mode
Estimate active audience size and observed cost. Push ordinary authors to active followers; retain high-fan-out author posts for read-time merging. Treat the threshold as a tunable cost decision.
Checkpoint batches
Use stable follower enumeration cursors and per-event progress. Insert unique viewer/post pairs; membership may change during fan-out, so final read eligibility remains mandatory.
Protect freshness
Track oldest publication age per shard, not just task count. Scale workers within cache/storage capacity and isolate celebrity or tenant hot spots.
Delete with a version
A tombstone wins over a delayed publish/fan-out event. Old workers may reinsert a reference, but hydration rejects deleted content; asynchronous cleanup reclaims those references.
8. Walk retrieval, ranking and stable pagination
For a first-page request, I merge the viewer’s materialized candidates with recent posts from a bounded set of high-fan-out followed authors and an own-post overlay. I deduplicate by postId before spending ranking capacity, then enforce current eligibility and hydrate in batches.
Bound retrieval
Fetch a limited candidate pool from each source using time watermarks. Cap followee scanning and use cached author timelines; do not issue one unbounded RPC per friend.
Enforce privacy
Check current deletion, audience, block and relationship state before exposing content. Use safe version-aware caching only if the agreed revocation bound permits it; fail closed for unknown private eligibility.
Rank with a deadline
Batch feature retrieval, score under a latency budget and apply diversity constraints. If ranking fails, use eligible chronological candidates; never bypass privacy because the model timed out.
Create a snapshot
Persist a bounded ordered ID list with viewer, model version and expiry. The cursor advances through this list; subsequent pages do not recompute all ranks and shift items across offsets.
Recheck each later page
Snapshot membership does not freeze authorization. Remove newly deleted/blocked posts, skip already-served IDs and refill only within bounded work. A short page is preferable to an unbounded slow request.
Separate refresh from continuation
Refresh creates a new session and may show new posts or a different order. Expose new-post availability instead of unexpectedly inserting content above the user during a scroll.
9. Compare push and pull under skew
Fan-out on write makes reads cheap until an author has millions of followers. Pure fan-out on read makes writes cheap but repeatedly merges many timelines. I select a hybrid and measure the cost of active-follower inserts against read-time retrieval, rather than applying one follower threshold forever.
| Decision | Chosen baseline | Alternative and trade-off |
|---|---|---|
| Ordinary authors | Push post references to active-follower candidate caches. | Inactive users can reconstruct on return; materializing all users wastes storage and work. |
| Celebrity authors | Pull recent posts during candidate retrieval. | A celebrity followed by nearly everyone can still become a hot timeline; replicate immutable timeline segments in cache. |
| Ranking | Bounded multi-stage retrieval and batch scoring. | A larger candidate pool may improve quality but consumes latency and feature-store bandwidth. |
| Cache loss | Controlled reconstruction with per-user coalescing and global budgets. | An unrestricted fallback to every friend’s timeline can collapse storage during a cache outage. |
| Regional serving | Local derived indexes; authoritative or safely versioned eligibility checks. | Asynchronous graph replicas trade latency for revocation lag; strict privacy requires a stronger read path. |
10. Recover without leaking or amplifying overload
I measure publish-to-visible age with sampled end-to-end probes, feed p99, candidate hydration hit rate, ranking timeout rate, empty/short pages, privacy-check failures and fan-out lag. Engagement improvements are evaluated with controlled experiments and guardrails, not just click count.
| Failure | Detection | Recovery and remaining limitation |
|---|---|---|
| Fan-out backlog | Oldest committed post not visible to sampled eligible followers. | Scale within limits, prioritize active users and add bounded pull recovery; do not discard unprocessed events without a recovery source. |
| Candidate cache loss | Miss rate and reconstruction concurrency spike. | Rate-limit rebuild, coalesce viewer requests and serve a smaller eligible feed; protect authoritative stores. |
| Ranker timeout | Scoring exceeds its deadline. | Fallback to chronological eligible candidates using the same privacy checks. |
| Delete or block races with fan-out | Stale reference exists after a newer status/graph version. | Reject at hydration; asynchronously remove references. Already delivered content cannot be recalled from a user’s memory/device. |
| Cursor expires or is tampered with | Session missing, signature invalid or viewer mismatch. | Require refresh or reject; never reuse another viewer’s snapshot. |
| New user has no graph | Empty candidate sources. | Show honest onboarding or separately labeled authorized discovery content; do not fabricate friend activity. |
| Feature pipeline lag | Model features older than allowed freshness. | Use validated defaults or fallback ranking, record feature age and roll back harmful model versions. |
11. End with the two invariants that matter most
The durable post and current audience rules are authoritative; feed caches and scores are rebuildable derived data. A stale candidate can reduce freshness but must not become a stale permission grant. Pagination stabilizes ranking within a session while retaining current authorization.
For the next deep dive I would choose either hybrid fan-out under celebrity skew or privacy-safe cache invalidation. Both connect directly to requirements; adding more model complexity before those are correct would not improve the design.
Technical references
Primary references explain underlying mechanisms. Workloads and architecture choices above remain proposed interview assumptions.