Skip to content
Back to the libraryProblem

Design: a news feed

Do the work when posts are written so opening the app is just reading a ready-made list of IDs, pull celebrity posts in at read time, then rank a shortlist inside a fixed time budget. Numbers, APIs, data model, ranking and follow-ups on one page.

The full lessons, with worked examples: Design: a news feed and Why your feed isn't in time order.

Requirements

Functional

  • Post text, photos and short videos.
  • Home feed: recent posts from people you follow, newest or best first.
  • Scroll for more without repeats or gaps.
  • New posts reach followers within seconds.
  • Deletes, blocks and privacy changes take effect everywhere quickly.

Non-functional

  • Fast: under 200 ms for most feed loads, under half a second.
  • Available: a slow ranker or a lost cache machine must not blank the feed.
  • : a post a few seconds late is fine; a lost post is not.
  • Spiky: festival nights and cricket finals bring several times normal traffic.

Out of scope (say so): ads, comments and likes counting (see How Instagram counts likes), search, stories.

Estimates

Say 50 crore daily users opening the app 10 times a day.

  • Feed loads: 500 crore a day ÷ about 86,400 seconds ≈ 60,000 a second, about 1.5 lakh at the evening peak.
  • Posts: 1 in 10 users posts once a day = 5 crore a day ≈ 600 a second.
  • Read-to-write ratio: about 100 to 1, so spend effort at write time to make reads cheap.
  • Fan-out inserts: 600 posts × about 200 followers each ≈ 1.2 lakh inserts a second, each tiny.
  • Feed cache: 500 IDs × about 16 bytes (ID plus time or score) ≈ 8 KB per user; × 50 crore ≈ 4 TB, about a hundred cache machines. Whole posts at about 1 KB each would be 500 times bigger, which is why lists hold IDs.
  • Celebrity cost: one post by a star with 30 crore followers at 10 lakh inserts a second across the fleet ≈ 5 minutes of fan-out for one post.
  • Ranking budget: about 30 ms to score about 200 candidates.

API

  • POST /v1/posts with caption, media links and audience → 201 with postId. Media is uploaded first to .
  • GET /v1/feed?cursor=:cursor&limit=20 → posts (author, caption, media links, counts) plus nextCursor. The encodes a position (score and post ID), never a page number.
  • GET /v1/feed/new-count?since=:cursor → how many newer posts exist, to show a "New posts" button instead of reshuffling.
  • DELETE /v1/posts/:postId → 204; hidden everywhere at once.
  • POST /v1/follows/:userId, DELETE /v1/follows/:userId → change the .
  • POST /v1/feed/events → batched impressions, likes, skips and hides for the activity log the ranker learns from.

Data model

  • posts (source of truth, by post_id): post_id (time-sortable), author_id, caption, media_urls, audience, created_at, deleted flag.
  • follows (the social graph): follower_id, followee_id, created_at. Indexed both ways: "whom do I follow" for pulls, "who follows me" for fan-out.
  • feed:<userId> in the feed cache: a capped, sorted list of post_id plus time or coarse score, about 500 entries, sharded by user ID with two or three copies.
  • users: follower_count and an is_celebrity flag (pull instead of push), last_active_at (skip fan-out to the inactive).
  • Post cache: post_id → the post's details, shared by every reader.
  • Ranked feed cache: the finished top 40 for each user, kept about 5 minutes so a reopen is instant.
  • Activity log: what was shown and what the user did, for training and for fresh features.

High-level design

New postSave onceQueue a jobPush IDsOpen appRead listHydrateScorePhotosPostingScrollingCDN (photos)Post serviceFan-out workersFeed serviceRankerPosts databaseFeed cachePost cache

Write path, at about 600 posts a second:

Step by step5 steps
  1. 1

    PriyaPost service

    New post with a photo

  2. 2

    Post servicePosts database

    Save post 9001 once

  3. 3

    Post serviceFan-out queue

    Job: deliver 9001 to Priya's 600 followers

  4. 4

    Fan-out queueFan-out workers

    Workers pick it up, skip inactive followers

  5. 5

    Fan-out workersFeed cache

    Put 9001 at the top of each list, trim to 500

Read path, at about 60,000 loads a second:

Step by step6 steps
  1. 1

    ArjunFeed service

    GET /v1/feed (first page)

  2. 2

    Feed serviceFeed cache

    His list: 500 post IDs (about 2 ms)

  3. 3

    Feed servicePost cache

    Latest from the 3 stars he follows (about 3 ms)

  4. 4

    Feed servicePost cache

    Details for 200 candidates, one batch (about 8 ms)

  5. 5

    Feed serviceRanker

    Score 198 after filtering (about 30 ms)

  6. 6

    Feed serviceArjun

    Top 20, a cursor and photo links (about 120 ms in all)

On open, the feed service reads the list, pulls in celebrity posts, hydrates the candidates in one batch, filters deleted and blocked posts, ranks, and returns 20 posts with a cursor. Photos come straight from a edge, never through the feed service.

Deep dives

Push, pull and the hybrid

  • (push): copy each new post ID into every follower's list. Reads become one lookup. Cost: write work and memory, much of it for people who never open the app.
  • (pull): store the post once; gather from everyone you follow at open time. Cheap writes, slow reads (one load might check 300 accounts).
  • Hybrid: push for ordinary accounts, pull for accounts above a threshold, say 10 lakh followers. A typical user follows a handful of stars, so pulling them at read time is a few lookups on posts that are certainly cached.
  • Make the threshold adjustable, and base it on fan-out cost, not just follower count. Lower it during spikes.
  • Skip inactive followers (say 30 days quiet, perhaps 30% of accounts); rebuild their list by pulling, once, when they return. That first open takes about a second instead of a fifth of one.

Hydration and hot posts

  • Feed lists hold IDs; fetches the posts in one batched read from the post cache. An edit or delete changes one place.
  • A viral post read lakhs of times a second is a . Keep several copies on different cache machines and a few seconds' copy in each feed server's memory.
  • When a hot copy expires, let one request refill it while the rest wait, a trick called .

Ranking

  • Gather a few hundred : the pushed list, celebrity posts, some suggestions. This is .
  • Filter seen, muted, blocked and deleted posts.
  • Quick trim: a small model with a few dozen cuts about 440 to about 150.
  • Careful scoring: the main ranker predicts several actions and combines them into one . Worked sum with weights like 0.5, comment 2, share 3, hide minus 20: like 72%, comment 20%, share 6%, hide 0.1% gives 0.36 + 0.40 + 0.18 − 0.02 = 0.92.
  • Feature families: closeness to the author, your taste, freshness with gentle , and early reactions.
  • Mix for variety (no author twice in a row, formats alternate), keep the top 40, cache for a few minutes.
  • Budget and fallback: precomputed features only; if the ranker is late, serve newest first rather than nothing.

Freshness, deletes and paging

  • Deletes and blocks: mark the post deleted at once (a ) and check visibility during hydration. Stale IDs in 600 lists are skipped everywhere immediately; a background job cleans the lists.
  • : show posters their own post in their feed straight away, even if fan-out lags.
  • Fan-out lag (post time to last insert) is a key metric with an alarm; add workers as the queue grows.
  • Paging uses a cursor after the last post seen, so new posts at the top never shift the next page. Repeats are caught by a small seen list, often a .

Cache failure

The feed cache is derived data. Each slice has two or three copies; if all are lost, the feed service rebuilds lists by pulling from the posts store and social graph, starting with users online now.

Trade-offs

  • Push vs pull: instant reads vs cheap writes; the hybrid chooses per author.
  • Freshness vs cost: delivery within 1 second needs far more workers than within 10. Most apps accept a few seconds.
  • Ranked vs chronological: ranking surfaces what people care about but buries some posts ("why didn't I see my sister's post?"). Offer a following-only, newest-first view too.
  • IDs vs copies: IDs keep lists small and edits cheap, at the cost of a hydration step.
  • Ranking depth vs latency: a heavier model is more accurate but eats the budget; two stages keep it affordable.
  • Exploration vs exploitation: showing some posts the model is unsure about costs a little engagement today and avoids feeds that only repeat past guesses.

Interview talking points

  • Lead with the 100-to-1 read-to-write ratio; it justifies precomputing feeds.
  • Store IDs, not posts: 4 TB vs hundreds of terabytes.
  • Raise the celebrity problem yourself and answer with the hybrid.
  • Walk the read path: list, celebrity pull, hydrate, filter, rank, page.
  • Use cursors, soft deletes and read-your-writes.
  • Put ranking in a fixed budget with a newest-first fallback.
  • Name the metrics: feed p99, fan-out lag, post cache hit rate.
For experienced engineers

Fan-out jobs for large accounts are split by follower ranges so they run in parallel rather than as one long job. Log features as they were at serving time, so training doesn't drift from what the ranker actually saw.

Follow-up questions

How do you decide who counts as a celebrity? By fan-out cost, roughly follower count, with a tunable threshold such as 10 lakh; some systems also weigh how many followers are active.

What if the feed cache is lost? It's derived, so rebuild by pulling from the posts store and the social graph, starting with users who are online. Replicas make that rare.

How do you add ranking without slowing the feed? Gather a few hundred candidates, trim with a light model, score the rest with precomputed features inside about 30 ms, and fall back to newest first if the ranker is late.

A brand-new post has no likes. How does it ever rise? Freshness gives it a head start with the author's closest followers; their early reactions lift or settle it. A small share keeps the model learning. This handles the for posts.

Someone deletes a post two minutes after it was pushed to 600 feeds. What happens? The post is marked deleted at once and filtered at hydration, so nobody sees it from that moment. The stale IDs are cleaned from lists later.

Why not page with page numbers? New posts arrive at the top while you read, so "page 2" shifts and you see repeats or miss posts. A cursor marks an exact position.