Facebook News Feed Interview Questions | JiQuest

add

#

Facebook News Feed

System design deep dive · HLD

Design a Facebook-style News Feed: full high-level design.

Requirements, back-of-envelope capacity estimation, a high-level design diagram, a deeper deployment architecture diagram that solves the celebrity fan-out problem, sequence diagrams for post creation and feed reads, and an entity-relationship diagram for the data model - with the reasoning an interviewer expects behind every box and arrow.

2BMonthly active users
~300Avg follows per user
<200msFeed load, p99
New postby regular user Fan-out servicepush or defer Feed cacheper-follower, capped Feed readyranked & returned Follow graphwho follows author read at next app open

1. Clarify requirements before drawing any box

A strong system design answer starts by pinning down scope out loud. For a news feed, that means separating "what must this feed do" from "how fast and how personalized must it be", and stating the scale assumptions that every later diagram depends on.

Functional requirements

Create a postText, photo, or video, with the media uploaded separately and referenced by ID.
Follow / unfollowDirected, asymmetric relationship - following someone doesn't require them to follow back.
Generate a ranked feedOn app load, return a personalized, ranked list of posts from people/pages a user follows.
Engagement affects rankingLikes, comments, and shares are both write targets and ranking-input signals.

Non-functional requirements

Low feed latencyFeed loads are on the critical path every time the app opens; target <200ms p99.
Eventual consistency is fineA feed that's a few seconds stale is acceptable; a feed that's down is not.
Availability over consistencyAn AP system: better to show a slightly stale or re-ordered feed than an error page.
Personalization at scaleEvery one of billions of users gets a distinct, independently ranked feed.
Explicitly out of scope Full threaded comment rendering, Stories/Reels-specific ranking, ad insertion and auction logic, and direct messaging are called out as adjacent systems rather than core requirements, so the core design stays focused on the feed itself.

2. Back-of-the-envelope capacity estimation

These numbers decide almost everything downstream: whether posts can be fanned out to every follower synchronously, how big the per-user feed cache needs to be, and why a single "one-size-fits-all" fan-out strategy breaks for the biggest accounts.

MetricAssumptionResulting estimate
Daily active users2B MAU, ~35% DAU/MAU ratio~700M DAU
Posts created / day~7% of DAU create at least one post/day~50M posts/day ≈ ~580 writes/sec avg, ~2,500/sec peak
Fan-out write amplification~300 avg follows/followers per user50M posts × 300 ≈ 15B fan-out writes/day ≈ ~174K/sec avg, ~500K/sec peak
Feed reads (app opens)700M DAU × ~8 opens/day~5.6B feed loads/day ≈ ~65K reads/sec avg, ~195K/sec peak
Feed cache storage700M users × ~500 capped entries × ~120 bytes/entry≈ 42 TB hot cache, sized for a Redis/RocksDB cluster
Post store growth50M posts/day × ~800 bytes metadata (media stored separately)≈ 40 GB/day metadata ≈ ~44 TB metadata over 3 years
Why this matters The 15B/day fan-out write figure is the single number that justifies almost every architecture decision below: it's why a naive "write every post into every follower's feed at post time" strategy is fine on average but catastrophic for the handful of accounts with tens of millions of followers - which is exactly the celebrity edge case the architecture section solves explicitly.

3. High-level design (HLD)

The HLD names the major components and the one-directional data flow between them - write path, fan-out, cache, and the read-time ranking call - without committing yet to replica counts, sharding, or regions, which belong in the architecture diagram in the next section.

Clientweb / mobile API gateway / LBauth, routing Post servicewrite path Feed serviceread path Fan-out servicepush or defer Graph storefollow edges Feed cacheper-user, capped ~500 Post storesharded + object store Feature pipelineKafka + feature store Ranking serviceML scoring, read time
Stateless servicesFast-path infraDurable storageAsync pipeline

What each box owns

Post service (write path)

Accepts a new post (text, plus a media reference if a photo/video was already uploaded to object storage), writes it durably to the Post store, and enqueues an async fan-out job. It returns to the client immediately after the durable write - fan-out happens after the response, never before it.

Fan-out service and graph store

For a normal account, reads the author's follower list from the Graph store and pushes a lightweight reference (post_id, author_id) into each follower's capped feed cache. For an account above the celebrity follower-count threshold, it skips fan-out entirely and marks the post for read-time merge instead - the full reasoning is in the architecture section below.

Feed cache and ranking service (read path)

The Feed service fetches the caller's precomputed feed_cache_entries, merges in any un-fanned-out celebrity posts, then calls the Ranking service to re-score the merged candidate set with fresh engagement signals before returning the top N - reads are never served in raw precomputed order.

Post store, graph store, and the async feature pipeline

The Post store is a sharded database (with an object store for media blobs) and is the durable source of truth. The Graph store holds follow edges optimized for "get everyone who follows X" at extreme scale. A Kafka-based pipeline aggregates likes/comments/shares into a feature store that the Ranking service reads from, so engagement never has to update ranking synchronously.

4. Detailed architecture diagram

The architecture diagram takes every HLD box and answers "how is this actually deployed, and what happens for the accounts with millions of followers?" - which is the specific detail an interviewer is checking for once they've accepted the high-level shape.

Edge layer GeoDNS / Anycast CDN (media only) API gateway + L7 LB Rate limiterper-user token bucket Region: us-east-1 Post svc ×8 pods Feed svc ×14 pods Fan-out svc ×6 pods Feed cache (32 shards) Region: eu-west-1 (active-active) Post svc ×4 pods Feed svc ×8 pods Feed cache (regional)async cross-regioninvalidation via pub/sub Celebrity fan-out-on-read path (>1M followers) Celebrity post index TTL cache, recent~50 posts/celebrity bypasses Fan-out svc entirely; Feed svc merges these at read time Storage tier Graph store128 shards, by follower_id Post storesharded + S3-style object store each shard: 1 primary + 2 read replicas Kafka: engagement events Stream workers Feature store Ranking svc ×10 pods
DecisionChoiceReasoning
Fan-out strategyHybrid: fan-out-on-write below ~1M followers, fan-out-on-read above itPure push would mean a single post from a 50M-follower celebrity generates 50M synchronous-ish writes; pure pull would mean every reader re-fans-out on every load. Hybrid pays each cost only where it's cheap.
Social graph shardingGraph store sharded by follower_id, not followee_idThe fan-out read pattern is "get all followers of author X"; the feed-build read pattern is "get everyone user Y follows" - sharding by follower_id keeps a user's own follow list co-located for the read path that runs on every feed load.
Feed cache shapeBounded ring buffer, ~500 entries/user, evict lowest rank_scoreKeeps per-user memory bounded regardless of how many accounts they follow; the ranking service can always re-score whatever subset is present rather than requiring a complete history.
Multi-region replicationFeed cache replicated asynchronously, cross-region pub/sub invalidationFeed freshness explicitly tolerates seconds of lag (an eventual-consistency requirement), so the write path never blocks on a cross-region round trip.

5. Sequence diagrams for the two critical flows

A sequence diagram is where an interviewer checks whether you actually understand call order, what is synchronous versus fire-and-forget, and exactly where the celebrity account skips a step.

5.1 Post creation and fan-out (normal user, write path)

Client Post service Post store Fan-out worker Graph store Feed cache 1. POST /posts {text, media_ref} 2. INSERT post(post_id, author_id, ...) 3. ack 4. enqueue fanout_job(post_id) via queue (async) 5. 201 Created {post_id} (doesn't wait for step 6-9) 6. SELECT follower_id WHERE followee_id=? (paged) 7. follower_id page (~300 ids, skipped if celebrity) 8. batched INSERT feed_cache_entry(follower_id, post_id) ×300

Step 5 does not wait on steps 6-8: the client sees success as soon as the post is durably stored. For a celebrity account, steps 6-8 never run at all - the fan-out worker checks the author's follower_count, marks the post for read-time merge instead, and stops, which is exactly the branch shown in the deep-dive section below.

5.2 Feed read at app load (read path, with ranking)

Client Feed service Feed cache Post store (celeb.) Ranking svc Feature store 1. GET /feed?cursor= 2. GET feed_cache_entries(user_id) LIMIT 500 3. ~500 (post_id, naive_rank) refs 4. recent posts by followed celebrities (not fanned out) 5. celebrity post refs 6. score(candidate_post_ids, user_id) - merged set 7. fetch fresh features (affinity, engagement) 8. feature vectors 9. ranked list with scores 10. 200 OK {posts[0..20], next_cursor}

Steps 2-5 build a merged candidate set from two sources: the precomputed feed_cache_entries (cheap, but only covers non-celebrity authors) and a direct, TTL-cached read of the celebrity post index (covers the accounts that were never fanned out). Steps 6-9 then re-score that entire merged set with fresh signals - which is precisely why a feed_cache_entries row can be seconds stale and it still doesn't matter: staleness in the cached rank is corrected by the ranking call on every single read.

6. Entity-relationship (ER) diagram and schema

The data model has to answer three questions: how is a follow relationship stored so that "get all of X's followers" stays fast even at 50M followers, how is a per-user feed kept bounded instead of growing forever, and where do engagement counters live without write-amplifying the hot posts table.

users PK id BIGINT display_name VARCHAR follower_count BIGINT is_celebrity BOOLEAN created_at TIMESTAMP posts PK post_id BIGINT FK author_id BIGINT content TEXT media_ref VARCHAR NULL created_at TIMESTAMP like_count BIGINT comment_count BIGINT is_deleted BOOLEAN follow_edges PK edge_id BIGINT FK follower_id BIGINT FK followee_id BIGINT created_at TIMESTAMP sharded by follower_id hash feed_cache_entries PK entry_id BIGINT FK user_id BIGINT FK post_id BIGINT rank_score FLOAT inserted_at TIMESTAMP capped ~500/user, evicts lowest rank_score 1N 1N 1N 1N one user authors many posts; one user follows many users via follow_edges; one post and one user each generate many feed_cache_entries

Key modeling decisions

follow_edges is sharded by follower_id, not an autoincrement idA celebrity's millions of incoming follow rows would otherwise land in a tight ID range on one shard; sharding by follower_id spreads the write pattern that actually matters (fan-out reads) evenly.
feed_cache_entries is intentionally bounded and lossyCapped at ~500 rows/user with oldest/lowest-rank_score eviction, because it's a cache/index over posts, not the source of truth - the Post store is.
Engagement counters are denormalized onto postslike_count/comment_count are updated asynchronously by stream workers consuming Kafka engagement events, not incremented synchronously on every tap - avoiding hot-row contention on viral posts.
is_celebrity on users drives the fan-out branchCrossing the follower_count threshold flips this flag, which is the single switch the Fan-out service checks to choose push vs defer.
Storage choiceUse whenWatch out for
Relational (Postgres/MySQL), sharded - posts & usersYou want a durable schema, moderate write volume relative to fan-out, and transactional updates to a single post row.Sharding posts at this scale still needs an app-level or Vitess-style resharding strategy as author activity grows unevenly.
Wide-column / graph store - follow_edges & feed_cache_entriesThe access pattern is pure key lookups at extreme write and read volume: "get all followers of X", "get feed_cache_entries for user Y".No cheap ad-hoc joins or aggregation; fan-out and eviction logic has to live entirely in the application layer.

7. Deep dives interviewers actually probe

How do you handle a celebrity account with 50 million followers without melting the fan-out pipeline?

Fanning a single post out to 50M feed_cache_entries rows - even asynchronously - would consume a huge slice of the entire system's write budget for one post. The fix is a hard branch in the Fan-out service based on the author's follower_count: below the celebrity threshold, fan out normally; above it, skip fan-out entirely and index the post in a small, TTL-cached "celebrity post index" instead. The Feed service then merges that index into every follower's candidate set at read time, so the cost is paid once per index write instead of once per follower.

def on_new_post(post):
    save_post(post)                                  # always durable first
    author = get_user(post.author_id)
    if author.follower_count > CELEBRITY_THRESHOLD:   # e.g. 1,000,000
        mark_as_celebrity_post(post)                  # indexed for read-time merge only
        return                                        # skip fan-out entirely
    enqueue(FanOutJob(post.post_id, author.id))        # normal async fan-out

Fan-out-on-write vs fan-out-on-read: what's actually being traded off?

Fan-out-on-write costs roughly 15B feed_cache_entries writes/day at today's scale (~300 followers avg), but keeps every feed read O(1) - just read your own precomputed list. Pure fan-out-on-read costs nothing at post time, but every single feed load would have to query and merge posts from everyone the reader follows (up to hundreds of accounts) at read time - multiplied across ~5.6B feed loads/day, that's far more expensive in aggregate than the write side. The hybrid design pays the write cost for the 99.9% of accounts where it's cheap in aggregate and reads stay O(1), and pays the read cost only for the tiny minority of celebrity accounts, whose recent posts can be cached once and shared across every reader instead of duplicated per-follower.

Why rank the feed with ML instead of just showing posts in reverse-chronological order?

Once a user follows hundreds of accounts that don't all post at convenient times, reverse-chronological wastes the limited attention of a single app session on whatever happened to post most recently, regardless of how relevant it is. Ranking instead predicts a probability of engagement per candidate post using features like recency decay, historical affinity with the author, post type (photo/video vs text), and short-term virality signals (recent like/comment velocity), then orders by that score - usually blended with a small amount of exploration/diversity to avoid feedback loops that over-favor a few accounts.

score = (
    0.35 * recency_decay(post.created_at) +
    0.30 * affinity(user_id, post.author_id) +
    0.25 * predicted_engagement_prob(user_id, post) +
    0.10 * post_type_boost(post.media_type)
)

What happens to a feed when a post is deleted after it's already been fanned out to a million feed_cache_entries?

Deleting a post marks it is_deleted in the Post store, but the system deliberately does not synchronously scrub every feed_cache_entries row referencing it - that would reintroduce the exact same write-amplification problem fan-out already has. Instead, the Feed service filters out deleted or tombstoned post_ids at read time while hydrating the candidate set (a cheap existence/status check), and stale rows are quietly evicted later during the cache's normal capacity-based eviction cycle. This is the eventual-consistency requirement showing up concretely: a deleted post can theoretically still occupy a feed_cache_entries slot for a while, but it will never actually be shown.

What is the single biggest bottleneck as this scales 10x?

Probably not the flagship celebrity case - that's already special-cased. The real risk is the "mid-tier influencer" band: accounts with, say, 50K-500K followers, too numerous to hand-classify as celebrities individually but numerous enough in aggregate that their combined fan-out write volume dominates the Graph store and Feed cache write path. A secondary bottleneck is feature store lookup latency in the Ranking service, since a larger merged candidate set (more celebrities followed, more mid-tier accounts) means more feature fetches per feed load. Mitigations include a dynamically adjusted celebrity threshold, tiered fan-out (fan out first to followers active in the last 7 days, defer the rest), and batching feature-store reads per request instead of one lookup per candidate post.

8. Summary: what a strong answer covers

Clarified scope before designingJustified every number with a calculationSeparated write-path fan-out from read-time ranking Solved the celebrity hot-key problem explicitlyMade engagement counters async and non-blockingCompared SQL vs NoSQL honestly
Interview tip When asked to design a news feed, the strongest signal is naming and solving the celebrity/high-fan-out edge case explicitly rather than assuming every user has ~300 followers - and being able to argue, with the write-vs-read cost trade-off in hand, why a hybrid fan-out strategy (not pure push, not pure pull) is the right default.
No comments
Leave a Comment