Skip to lesson
‹

Courses / System Design Interview / Chapter 29

Module 3 · Design Real Products · Chapter 29 of 39

Design a News Feed

20 sections · ~38 min✓ Fact-checked

Builds on Back-of-the-Envelope Estimates, Abstractions That Hide the Network, Content Delivery Networks, Distributed Caches, Message Queues, Publish-Subscribe, Sharded Counters, Design a Video Streaming Platform and Design a Microblog. Next up: Design a Photo-Sharing App.

Part 1 · Requirements and Scale

The brief

Diwali morning, 8 am. Meena opens the app for the first time since last night, and her 400 connections have posted about 2,000 things since then: her cousin Asha’s wedding photos from 3 am, forty match updates from cricket pages, sale posts from shops she followed years ago. In newest-first order, Asha’s photos sit on screen nine, and Meena never gets there. Last year the team fixed that by re-ranking on every scroll. Page 2 then repeated three posts from page 1, and at the 9 pm peak the ranking fleet ran out of capacity and feeds stopped loading. This chapter builds a feed that ranks well, pages cleanly and stays up.

The product: the news feed of a social app with 300 million daily users. People see posts from friends, pages and groups, ordered by what they are most likely to care about, and the feed keeps loading as they scroll. You are designing how the feed is gathered, ranked and paged.

From timeline to feed

Design a Microblog built a timeline: posts from accounts you follow, newest first. A news feed draws on the people, pages and groups you are connected to, but orders posts by how likely you are to care about them. It mixes text, photos, videos and links, and it keeps loading as you scroll.

Think of a friend who reads every newspaper in town and hands you the five stories you would actually want. That friend is the ranking, and in a feed ranking is the feature. Three things make it harder than a timeline. The order depends on the viewer, so no two people share a list. It depends on signals that change by the minute, such as how many likes a post has collected. And Meena still expects her scrolling to stay stable while all of that moves underneath.

The scope below keeps ranking and paging at the centre.

In scopeOut of scope
A ranked feed of posts from friends, followed pages and groupsCreating posts: a post service already exists
Infinite scroll with no repeats and no gapsChoosing and inserting ads (leave a slot for them)
Posts with text, photos, videos and link previewsComments and messaging
A "new posts" prompt when fresh posts arriveTraining the ranking model; we call it as a service
Hide a post, or see less from an authorNotifications

The items on the right either exist already or plug into fixed slots, so none of them changes how the feed is built.

Ranked, chronological, or both

Many apps offer a "latest" view beside the ranked one. That view is the microblog timeline with a post id as its cursor; everything in this chapter is about the ranked default.

How well it must work

A feed is judged on feel: the first screen must appear quickly and scrolling must never stall. The requirements turn those feelings into numbers.

  • Fast first page. The server returns the first 10 posts in under 300 ms at p99, the time that 99 of every 100 requests beat; images then load from a nearby CDN.
  • Smooth scrolling. Each later page returns in under 100 ms, so the next screen is ready before the thumb gets there.
  • Fresh enough. A friend’s new post becomes eligible for your feed within about a minute.
  • Degrade, do not fail. If ranking is slow or down, show a feed in time order rather than an error.
  • Privacy is checked at read time. A post shared with friends only must never reach someone who has since been unfriended.

The assumptions below drive every estimate.

AssumptionValue
Daily active users300 M
Feed sessions per user per day8
Pages per session (10 posts each)3
New posts per day50 M
Connections per user (friends, pages, groups)400 on average

Together they give the request rate, by the method of Back-of-the-Envelope Estimates: 300 M × 8 sessions × 3 pages = 7.2 B page loads a day, about 83,000 a second on average and roughly 250,000 a second around the 9 pm peak (3×). The next page asks whether the precomputed data behind those pages fits in memory.

Estimates: memory for candidate pools

A ranked feed needs something to rank. For each user we keep a candidate pool: recent posts from their connections that they have not seen yet. Meena’s pool holds the posts her 400 connections made since she last looked. Pools are kept up to date as posts are created and ranked from at session start, so first check that they fit.

Try it first: each pool keeps 500 entries of 32 bytes (post id, author id, time, affinity, flags) for each of 300 M daily users, with two copies. How much memory is that?
  • 500 × 32 B = 16 KB per user.
  • 16 KB × 300 M = 4.8 TB for one copy.
  • × 2 copies ≈ 10 TB: about 150 servers with 64 GB free each, before growth.

Ten terabytes of memory is affordable at this size but not free. Keep in memory only the pools of people who open the app most days, and move the rest to an SSD-backed store, where a read costs a millisecond or two instead of a fraction of one. A pool nobody has read for a week can be dropped and rebuilt on the next visit.

The other estimates fill in the rest of the load.

QuantityWorkingResult
Feed page loads7.2 B a day ÷ 86,400 s≈ 83,000 /s, peak ≈ 250,000 /s
Post lookups to fill pages83,000 pages × 10 posts≈ 830,000 /s, nearly all from cache
Impression events7.2 B pages × 10 posts = 72 B a day≈ 830,000 /s, the biggest write stream
Candidate inserts50 M posts × 400 connections ÷ 86,400 s≈ 230,000 /s
Media bandwidth83,000 pages × 5 images × 150 KB≈ 62 GB/s, about 500 Gbps on average
Scoring work2.4 B sessions × 500 candidates ÷ 86,400 s≈ 14 M scores /s, if we rank once per session

Two rows shape the design: scoring work, which decides when ranking runs, and media bandwidth, which must go through Content Delivery Networks, never through feed servers.

Part 2 · API, Data and the Pipeline

The API

With the load known, the API stays small: fetch a page, ask whether anything new has arrived, and report what the user saw or hid. All the ranking happens behind these calls.

http
GET  /v1/feed?limit=10                    -> 200 {"items": [...], "next_cursor": "c2Vzcz..."}
GET  /v1/feed?limit=10&cursor=c2Vzcz...   -> 200 {"items": [...], "next_cursor": "..."}
GET  /v1/feed/new-count?cursor=c2Vzcz...  -> 200 {"count": 4}
POST /v1/feed/impressions  {"post_ids": ["p81", "p77"], "session": "s_3f"}     -> 204
POST /v1/feed/hide         {"post_id": "p81", "reason": "see_less_from_author"} -> 204
PUT  /v1/posts/{post_id}/reactions/me      {"type": "like"}                     -> 204

Each item carries the post, its author, counts and media URLs that point at the CDN.

The cursor is opaque to the client: the server packs into it everything it needs to continue the same ranked list, which the paging deep dive explains. Impressions, the record of which posts actually appeared on screen, go in batches every few seconds rather than one call per post. They feed ranking and stop the app from showing Meena the same post again tomorrow.

Each item in a page carries everything the phone needs to draw one card:

Media URLs are sized for the screen. A phone 1080 pixels wide gets the 1080-pixel copy, about 150 KB, not the 4000-pixel original, and private posts get signed CDN URLs that expire after a few minutes. Videos start muted at a low quality from short segments, as Design a Video Streaming Platform describes. The next page covers the data behind these calls.

Data model

The feed reads data that other services own: posts, people and the connections between them. It adds two things of its own, the candidate pool per user and an event log of what users did.

usersuser_id (pk)nameavatar_key
postspost_id (pk)author_idaudience (public, friends, group)type (text, photo, video, link)media_keyscreated_at
connectionsuser_id (pk part 1)target_id (pk part 2)kind (friend, page, group)affinityupdated_at
candidate_pooluser_id (pk part 1)post_id (pk part 2)author_idaffinityinserted_atseen
interactionsuser_idpost_idaction (view, like, comment, share, hide)at

Each kind of data goes where its access pattern fits:

DataStoreWhy
PostsSharded key-value store with a post cacheRead by id, close to a million times a second
Connections and affinityWide-column or graph store, keyed by userOne read per feed build: all of my connections
Candidate poolsIn memory for active users, SSD-backed for the restMany small appends; read when a session starts
InteractionsAppend-only log, then a data warehouseHuge volume; used to compute signals and train models
Engagement countsSharded countersA viral post gets thousands of reactions a second

Only the pools are written on every new post; everything else is read-mostly or append-only. Affinity is a number from 0 to 1 for each pair of user and connection: how close they seem, from likes, comments, messages and profile visits over the last few weeks. Meena’s affinity to Asha is about 0.9. It changes slowly, so a daily batch job over the interactions log is enough.

A pool has a simple life cycle:

  • Fills: a fan-out worker appends a post id when one of your connections posts.
  • Drains: impressions mark entries as seen, and entries older than about three days expire.
  • Stays small: once a pool holds 500 entries, the lowest-affinity entries go first, so someone with 5,000 connections keeps the people closest to them.

High-level design

Those pieces form a pipeline. The first stage runs when posts are created; the rest run when Meena opens the app. Pools fill exactly as Design a Microblog fills timelines: fan-out on write for most authors, pull at session start for pages above a follower threshold, chunked jobs and all. This chapter does not repeat that machinery; its depth goes to what happens after the pool is full.

From new post to Meena’s screen
  1. 1Gather candidatesher pool, big pages pulled now, recommended and trending posts
  2. 2Filterdrop seen, hidden, deleted and no-longer-visible posts
  3. 3Fetch signalsaffinity, post age, engagement so far, content type
  4. 4Score and re-ranka score per post, then rules for variety
  5. 5Cache and servestore the ranked list for this session; return 10 posts at a time

The scene follows Meena’s first page, her second, and an evening when ranking falls behind.

Animated sceneA feed that loads even when ranking is slow0:33

8 am: Meena opens the app, and no ranked list exists for this session yet

Read it as steps

Watch for the 150 ms timeout: the morning page is ranked and cached, and at the 9 pm peak the feed still loads in time order.

The first page pays for ranking once, and later pages are cache reads. When ranking misses its budget, the feed falls back to time order instead of failing, and feed servers send small JSON while the CDN sends the heavy bytes, so 500 Gbps of media never needs 500 Gbps of feed servers.

Where candidates come from

The pipeline’s first stage decides what can be ranked at all. So far every candidate came from the viewer’s connections; most feeds mix in other sources, each producing a short list at session start that joins the pool before ranking.

SourceWhat it addsHow it is built
ConnectionsPosts from friends, followed pages and groups: most of the feedFan-out into the pool, as in Design a Microblog
RecommendedPosts from accounts the viewer does not follow but may likeA recommendation service returns a few hundred, matched by topic or by what similar people engaged with; refreshed every few hours
Trending nearbyWhat many people in the region are engaging with nowA stream job keeps the top posts per region and language: one shared list, refreshed every few minutes
AdsPaid posts in fixed slotsThe feed reserves positions, say 4 and 12; the ads system fills them when the page is served

Each candidate carries its source, and the ranker scores all of them on the same scale. Re-ranking rules then cap each source, for example at most 3 recommended posts in every 10, so Asha is never crowded out by strangers.

Cold start is the empty-pool problem. A new user has no connections, no affinity and no history, so start with trending posts in their region and language, ask for a few interests at sign-up, and suggest people from their contacts if they allow it. Their first likes and hides reshape the very next session. A new post has the same problem: with few impressions its engagement rate means little, so show it to a small sample first and widen if people respond. Those impressions are the next page’s subject.

Impressions: the biggest write stream

Ranking learns from what people saw, so every post that appears on screen produces an impression event. 7.2 B pages a day × 10 posts is 72 B impressions, about 830,000 events a second, the biggest write stream in the system. New posts arrive at under 600 a second; impressions arrive more than a thousand times faster.

Nobody reads these events one by one, so they never touch a database. The impressions endpoint checks a batch, appends it to a partitioned, replicated log, as in Publish-Subscribe, and returns 204. Phones send one batch every few seconds, so the endpoint handles requests, not single events. Three consumers read the log at their own pace:

ConsumerWhat it doesWhat it feeds
Seen markerMarks pool entries as seen, per userThe next session skips them
Stream counterCounts impressions per post in one-minute windowsThe engagement rate’s denominator, in the signal store within seconds
Warehouse loaderWrites hourly files to the data warehouseDaily affinity jobs, model training and A/B test results

One write feeds three jobs, and none of them slows the phone. Size the log from bytes, not events: at about 100 bytes an event, that is roughly 83 MB/s on average and 250 MB/s at the evening peak. One log partition handles about 10 MB/s, so the peak needs 25 partitions; plan for about 50 to keep 2× headroom for spikes and replays. Key them by user id, so one user’s seen marks stay in order on one partition.

At least once, counted carefully

A phone resends a batch when the reply is lost, so events arrive at least once. Give each batch an id and let the counter skip ids it has seen in the last hour. A count off by a fraction of a percent never changes a ranking.

Part 3 · Deep Dives

Precompute or rank on request

Core deep dive

Speed, freshness and cost all come back to this one choice: when the expensive scoring work runs, and how many times.

Think of a restaurant. It can cook every dish before opening, cook each plate when ordered, or prepare ingredients in the morning and cook when the table sits down. A feed has the same three choices, and the choice decides latency, freshness and the bill.

Rank everything offlineRank on every requestPool on write, rank per session
When ranking runsA batch job every few hoursEach page loadWhen a session starts
Latency per pageLowest: every page is a cache readAbout 100 ms of scoring on every pageAbout 100 ms on the first page, then cache reads
FreshnessHours oldUp to the secondFresh at session start
Wasted workFeeds built for people who never open the appThe same posts re-scored on every scrollLittle
Scoring workEvery user every few hours, visit or not≈ 42 M scores a second≈ 14 M scores a second
When ranking runs

Rank everything offlineA batch job every few hours

Rank on every requestEach page load

Pool on write, rank per sessionWhen a session starts

Latency per page

Rank everything offlineLowest: every page is a cache read

Rank on every requestAbout 100 ms of scoring on every page

Pool on write, rank per sessionAbout 100 ms on the first page, then cache reads

Freshness

Rank everything offlineHours old

Rank on every requestUp to the second

Pool on write, rank per sessionFresh at session start

Wasted work

Rank everything offlineFeeds built for people who never open the app

Rank on every requestThe same posts re-scored on every scroll

Pool on write, rank per sessionLittle

Scoring work

Rank everything offlineEvery user every few hours, visit or not

Rank on every request≈ 42 M scores a second

Pool on write, rank per session≈ 14 M scores a second

The third column wins for most products. Keep pools up to date as posts are created, rank once when a session starts (roughly one sitting with the app), cache that ranked list for about 15 minutes, and serve every scroll from it. There are 7.2 B page loads but only 2.4 B sessions a day, so this does a third of the scoring of ranking per page, and it never reshuffles posts under Meena’s thumb.

The advice flips at the edges. A "latest" view needs no ranking at all. A short-video app where each swipe changes what the viewer wants may re-rank every few items, and pays for it. And users who open the app every morning at 8 can have their first list built a few minutes earlier, so their first page is instant, while monthly visitors get theirs on demand. The next page sets the budget for that per-session work.

Ranking once per session: the budget

Ranking per session moves the cost to one moment, the first page, so that moment needs a budget. 2.4 B sessions a day is about 28,000 session starts a second, and about 83,000 at the 9 pm peak; at 500 candidates each, that is about 42 M scores a second at the peak.

The first page must return in 300 ms at p99, and each step gets a share of it:

Step on the first pageTypical timeLimit
Gather: read the pool, pull big pages, add recommended posts20 ms50 ms
Fetch signals for 500 candidates30 ms50 ms
Score 500 candidates100 ms150 ms timeout
Re-rank, cache the list, hydrate 10 posts20 ms30 ms
Total on the server≈ 170 msunder 300 ms with room for the network

Scoring takes the largest share, so it gets a hard timeout. If it runs out, serve the candidates in time order and rank properly on the next session: a plain feed now beats a perfect feed in three seconds. The cached list itself is cheap: 500 ids × 8 bytes = 4 KB a session, and 83,000 starts a second kept for 15 minutes is about 75 M lists, roughly 300 GB.

Three cases need rules. A user who reopens within 15 minutes continues the same list. A pull to refresh re-ranks, but at most once a minute per user, so an impatient thumb cannot multiply the load. And new candidates that arrive mid-session wait behind a "new posts" prompt rather than reshuffling the list.

Two passes cut the bill

Larger systems score in two passes: a cheap model trims 500 candidates to about 100, and a heavier model orders those. If the heavy model costs ten times as much per candidate, that is 500 + 1,000 = 1,500 units instead of 5,000, about 70% less.

Ranking: signals and a score

The budget says when scoring runs; this page says what it computes. Each candidate gets a score from a handful of signals, measurable facts about the viewer, the author and the post, and the feed shows the highest scores first. Five signals do most of the work:

  • Recency: how old the post is.
  • Affinity: how close the viewer is to the author, such as 0.9 for Meena and Asha.
  • Engagement: how others reacted, relative to how many saw it.
  • Content type: what this viewer tends to engage with, such as videos watched to the end.
  • Negative feedback: hides, "see less" and reports, which count against.

Engagement must be a rate, not a count. 120 likes from 1,000 viewers (12%) beats 2,000 likes from 100,000 viewers (2%), because the first post is landing with the people who see it. The rate is compared with other recent posts to get a value from 0 to 1, and a post with only a few views gets a neutral value until more arrive. That rate is why the impressions stream exists.

A simple, explainable score multiplies a weighted sum of signals by a time decay, so every post loses half its score every 6 hours:

text
score = (0.5 × affinity + 0.3 × engagement + 0.2 × type_match) × 0.5 ^ (age_hours / 6)

Here are three posts from Meena’s 8 am pool:

PostAffinityEngagementType matchAgeScore
A. Asha’s wedding photos0.90.40.75 h0.71 × 0.56 = 0.40
B. A cricket page’s viral video0.20.950.92 h0.565 × 0.79 = 0.45
C. An acquaintance’s text post0.30.10.420 min0.26 × 0.96 = 0.25
A. Asha’s wedding photos

Affinity0.9

Engagement0.4

Type match0.7

Age5 h

Score0.71 × 0.56 = 0.40

B. A cricket page’s viral video

Affinity0.2

Engagement0.95

Type match0.9

Age2 h

Score0.565 × 0.79 = 0.45

C. An acquaintance’s text post

Affinity0.3

Engagement0.1

Type match0.4

Age20 min

Score0.26 × 0.96 = 0.25

Time order would show C, B, A, with Asha last. The score shows B, A, C: the fresh but dull post drops, and Asha’s photos move up despite being five hours old. Change the weights and the feed’s personality changes, which is why new weights go to a small share of users first. Re-ranking then adds variety: no two posts in a row from one author, and a cap on videos per page.

Why offsets break

A ranked list is only useful if Meena can scroll through it cleanly, and feeds change while she reads: new posts arrive, scores move, and she hides things. Offset pagination, "give me items 11 to 20", counts positions in a list that will not hold still.

Predict before you watch: Meena sees posts A to J on page 1. Then 3 new posts rank in at the top. With offset paging, what does page 2 (items 11 to 20) start with?
  • H, I and J again: everything shifted down by three, so items 11 to 13 are posts she already saw.
Animated sceneOffset paging repeats; a cursor does not0:24

Offset paging: page 1 is items 1 to 10, posts A to J

Read it as steps

Watch page 2 in both runs: offsets serve H, I and J again after new posts arrive, while the cursor reads on from the frozen list.

Offsets count positions in a list that keeps moving, so new posts cause repeats and removed posts cause silent gaps. A cursor names a position in one frozen list, so later changes cannot move it. The next page shows what goes inside that cursor.

Cursors into a frozen list

A cursor is an opaque token that says where you are in a particular list. In the microblog’s timeline it can be the last post id shown, because that list is sorted by id. A ranked list is not. The last score shown fails too: scores change between pages, and ties make "after 0.45" ambiguous. So the cursor holds the id of the session’s ranked list and a position in it.

text
cursor = base64( sign({"list": "L7", "pos": 10, "user": "u42", "expires": "2026-10-16T09:15Z"}) )

The server signs it so clients cannot edit it or read another user’s list, and because clients never look inside, the server can change its contents later without breaking old app versions.

Lists expire, so the cursor must survive that. If list L7 is gone when page 2 is requested, the server ranks a new list and skips any post ids already shown in this session, which it keeps in a small seen set: about 30 ids for three pages, a few hundred bytes. Meena sees no repeats, even across a rebuild.

Freshness works with the frozen list, not against it. While the app is open, it calls new-count every minute or two and shows "4 new posts". It inserts them only when Meena taps or pulls to refresh, which starts a new list at position 0, because reshuffling under someone’s thumb loses their place.

A second line of defence

The app should also drop any post id it has already rendered. It costs a few lines of code and hides any server-side slip.

With ranking and paging in place, the next question is what happens when the parts they depend on fail.

Part 4 · Failures and Trade-offs

Bottlenecks and failures

Every dependency of the feed can be slow or down, and the rule for each is the same: return something useful within the time budget. The table lists each failure, what the user sees, and how the design copes.

What goes wrongWhat the user seesHow the design copes
Ranking slow or down, or signals missingA slightly plainer feedDefaults for missing signals; a 150 ms timeout, then candidates in time order
Feed cache node lost at the 9 pm peakA slower first page for some usersRe-rank from the pools on demand; coalesce repeated requests for the same list, as in Distributed Caches, and rate-limit rebuilds
A candidate pool node failsNothing, or a plainer feed for a whileIts second copy serves reads; a lost pool is rebuilt by pulling recent posts from the user’s connections
A post goes viralNothing, if handledThe post object is replicated in the cache; counts come from Sharded Counters, refreshed every few seconds
Fan-out falls behind after a big matchNew posts appear lateHandled as in Design a Microblog: more workers, active users first; the "new posts" prompt hides the delay
Someone is unfriendedTheir friends-only posts must vanishVisibility is checked at hydration, never trusted from the pool
The impressions log is slow or downA few seen posts may return next sessionPhones keep batches and resend them; ranking uses the last counts; consumers catch up
A zone or a whole region failsA zone: nothing visible. A region: a slower, plainer first pagePools and caches keep a replica in another zone. A failed region’s users move to another, which rebuilds pools at a capped rate and serves time order until it catches up

Almost every failure ends in the same place, a plainer feed in time order, which is why that fallback is designed in from the start rather than bolted on.

Trade-offs, quality and cost

A feed that never fails can still fail its users. The big product trade-off is engagement against variety: a feed tuned only for clicks fills up with one kind of post. The re-ranking rules help, and so do a few slots for newer authors, so the system keeps learning what Meena likes.

Clicks alone are a poor target because they reward bait, so every ranking change runs as an A/B test: a small random share of users gets the new version, and a mix of metrics is compared with everyone else before it rolls out.

MetricWhat it tells you
Sessions per user per dayWhether people come back
Hides and "see less" per 1,000 impressionsWhether the feed annoys people
Share of posts from close connectionsWhether friends get buried under pages
Retention after 30 daysThe long-term effect that short-term clicks can hide

A change that lifts clicks but also lifts hides, or lowers 30-day retention, is not shipped. In production, three signals show whether the system itself is healthy:

Media egress through the CDN dominates the bill: about 500 Gbps on average, some 160 PB a month. Ranking compute comes second, which is why ranking once per session instead of once per page matters.

Part 5 · Interview kit

The 45-minute answer

Here is the whole answer on the interview clock: about 40 minutes of design once introductions are done.

MinutesWhat you say and drawKey number or decision
0–5Ranked feed from friends, pages and groups; infinite scroll with no repeats; a new-posts prompt. Out: creating posts, choosing ads, training models.First page p99 under 300 ms; later pages under 100 ms
5–8Page loads, impressions and scoring work.83,000 pages/s, 250,000 at peak; 830,000 impressions/s
8–13GET /feed with an opaque cursor; impressions in batches. Candidate pools, connections with affinity, an interactions log.500 candidates × 32 B per user ≈ 10 TB with two copies
13–22Fan-out fills pools (as in the microblog); at session start: other sources, filter, signals, score, re-rank, cache the list. Hydrate from the post cache; media from the CDN; impressions to a log.Rank once per session; keep the list 15 minutes
22–37When ranking runs and its budget, then the score, then cursors into the frozen list.Per session: 14 M scores/s, not 42 M; 150 ms timeout
37–40Ranking down, a region failing, what you monitor, engagement against variety.Alert on the fallback rate

Pick the deep dive from the interviewer’s questions. Latency or cost: when ranking runs. Why one post beats another: the score and its signals. Infinite scroll or duplicates: cursors. A brand-new user: candidate sources and cold start.

Say
"I keep a pool of about 500 candidates per user, filled by fan-out, and rank it once per session: 14 M scores a second instead of 42 M."
Say
"The cursor points into the session’s ranked list, frozen for 15 minutes, so new posts wait behind a prompt instead of shifting pages."
Say
"If ranking misses its 150 ms budget, I serve the pool in time order: a plainer feed beats an error."
Avoid
"A machine learning model decides." Name the signals and say what the score does with them.
Avoid
"Page 2 is items 11 to 20." In a feed that re-ranks every minute, offsets repeat and skip posts.

Interview drill

Interviewers often change the problem halfway to see whether the design bends or breaks. Answer each one before opening it.

A cricket final ends at 11 pm and feed opens jump to 10× normal for 15 minutes. What breaks, and what do you change?
  • Session starts carry the cost: ranking each one fresh would mean about 140 M scores a second.
  • Reuse a ranked list when a user reopens within minutes, and pre-rank for the most active users before known events.
  • Shed quality before failing: only the cheap first-pass model, or time order for part of the traffic.
Five friends share the same article. Product wants one card: "Asha and 4 others shared this". What changes in the data model?
  • Key pool entries by the root post or the link’s canonical URL: (user_id, root_id, sharers, latest_at).
  • A reshare updates the existing entry instead of appending a new one; the number and closeness of sharers become a signal.
  • Seen sets and cursors use root_id, so the article does not return as another friend’s share.
Finance wants ranking compute cut in half without hurting feed quality. What do you change?
  • Rank in two passes: a cheap model trims 500 candidates to about 100 for the heavy model.
  • When a user returns within 30 minutes, score only the new candidates and merge them in.
  • Prove it with an A/B test: hides per 1,000 impressions and 30-day retention must hold, not just clicks.
Common miss
Ranking on every page. It triples scoring work and reshuffles posts under the user’s thumb; rank per session and page through the cached list.
Common miss
Ignoring the write side. Impressions, about 830,000 a second, outnumber every other write; they belong in a log, not a database.

Wrap-up

Use this page to revise before a mock round.

Next, Design a Photo-Sharing App reuses this feed and moves the weight to the upload side: signed URLs, resize workers, a safety scan and petabytes of images.

Part 6 · Check yourself

Quick check

Q1A teammate’s feed re-ranks on every request and pages with ?offset=10. Users report seeing posts from page 1 again on page 2, mostly in the evening. What is the cause, and the fix?

Think it through, then check.

Q2Product wants 30% of each feed to come from accounts the user does not follow. What changes?

Think it through, then check.

Q3A teammate wants to rank engagement by raw like counts, because they need no impression data. What do you gain, and what do you lose?

Think it through, then check.

Q4At 9 pm the ranking service is overloaded and most calls time out. What does Meena see, and what stops the overload from getting worse?

Think it through, then check.

Q5Which building block carries impressions from phones to ranking and the warehouse, and why not a database table?

Think it through, then check.

Next chapter · 30 of 39Design a Photo-Sharing App~37 min · 2 animated scenes