Vibe Engines
YouTube
AI System Design

Design a Recommendation System

Step 1 / 9

Learn AI system design by building a large-scale recommendation system step by step.

The numbers to beatmillionsitems, each a vector1 vectorper userdistance= predicted affinity

The whole design, in writing

Learn AI system design by building a large-scale recommendation system step by step. An interactive guide covering the two-stage retrieve-and-rank funnel, two-tower embedding retrieval with ANN, a heavy ranking model, the feature store and its offline/online split, filtering business rules, and the feedback loop that keeps recommendations fresh.

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

Pick the best few from millions

A user opens the app. Somewhere in a catalog of millions of items are the ten they’d most want to see right now. You have tens of milliseconds to find them. You can’t score millions of items per request — but you can’t guess, either. How do you do this fast and well?

Useropens the app
New in this step: User.

Use a funnel. A cheap, fast stage narrows millions to a few hundred plausible candidates; a heavy, accurate stage ranks those few hundred precisely. Cheap-and-wide, then expensive-and-narrow — the pattern behind every large recommender.

What the new pieces do

Userclient
A person who opens the app expecting a feed of items they’ll actually like — picked from millions.

Step 1 · The skeleton

A request and a funnel

The user opens the app and needs a feed. We can’t run a heavy model over millions of items in the time budget. What overall structure makes "best ten from millions" feasible?

Useropens the appRecs Gatewayorchestrates
New in this step: Recs Gateway.

You must return the best 10 of millions in ~tens of ms. What’s the shape?

  1. Running a heavy model over millions of items per request is impossible in the latency budget. You must shrink the set first.

  2. Popularity is a fine fallback but ignores the individual — it’s not personalization, and engagement suffers. You need per-user relevance.

  3. Stage 1 narrows millions to hundreds cheaply; stage 2 ranks those hundreds with a heavy model. Each stage is sized for its job.

A Recs Gateway runs a two-stage funnel for every request: candidate generation (millions → hundreds, fast) then ranking (hundreds → ordered, accurate), with filtering before the final list ships to the user. The shape is the whole idea.

What the new pieces do

Recs Gatewaybackend
Runs the funnel for each request: fetch candidates, rank them, apply rules, return the final list.

Step 2 · Represent taste

Embeddings for users and items

To find items a user will like, you need to compare a user against items numerically. "Action movies" and "this thriller" need to be close somehow. How do you represent taste so similarity is computable?

Useropens the appRecs Gatewayorchestrates
The system as it stands at this step.

How do you make "this user" and "this item" comparable for similarity?

  1. Tags are coarse and miss latent taste — two thrillers can feel totally different. Learned embeddings capture nuance hand-tags can’t.

  2. Train embeddings from engagement so a user vector lands near the item vectors they’d enjoy. Similarity becomes distance in that space.

  3. IDs carry no notion of similarity — item 5012 isn’t "near" item 5013. You need a learned space where proximity means relevance.

The system learns embeddings: users and items map to vectors in a shared space where a user sits near the items they’d engage with. These come from training on past engagement, so proximity encodes learned taste — not hand-typed tags.

  • millionsitems, each a vector
  • 1 vectorper user
  • distance= predicted affinity

Step 3 · Narrow the field

Two-tower retrieval with ANN

You have a user vector and millions of item vectors. You need the few hundred closest — in milliseconds, per request. Comparing against every item exactly is too slow. How do you retrieve candidates fast?

Candidate Gentwo-tower ANNItem Indexembeddings + ANN
New in this step: Candidate Gen, Item Index.

Find the few hundred closest items to the user vector, fast. How?

  1. Exact scoring over millions per request blows the latency budget. At this scale you use an approximate index.

  2. A "two-tower" model produces the user vector live and item vectors offline; an ANN index returns the nearest few hundred in milliseconds.

  3. Category filters are a blunt pre-filter that miss cross-category gems and still leave too many to score exactly. ANN over embeddings is the scalable answer.

A two-tower model has a user tower (computed live from the request) and an item tower (item vectors precomputed offline and stored in the Item Index). Candidate Generation embeds the user, then runs ANN search to pull the nearest few hundred items in milliseconds. Recall stage: don’t miss the good ones.

What the new pieces do

Candidate Genservice
Narrows millions of items to a few hundred plausible candidates fast, using embedding similarity.
Item Indexindex
Vector index of every item’s embedding, so candidate generation is a nearest-neighbour lookup.

Step 4 · Rank with care

The heavy ranking model

Retrieval handed back ~hundreds of candidates, ordered only by rough embedding similarity. Similarity isn’t the same as "will this specific user engage right now." How do you get the order right?

Recs GatewayorchestratesCandidate Gentop few hundredRanking Modelpredict engage
New in this step: Ranking Model.

You have ~hundreds of candidates. How do you order them precisely?

  1. Embedding distance is a coarse recall signal, not a precise engagement prediction. The top few hundred need a real scoring model.

  2. Popularity ignores the individual and context. The whole point of ranking is per-user, per-moment precision.

  3. A ranking model scores P(engage) for each candidate using many user/item/context features — affordable now because there are only hundreds, not millions.

A heavy Ranking Model scores each candidate’s probability of engagement (click, watch, purchase) using rich features — user history, item attributes, context (time, device), cross features. It’s far too expensive to run over millions, but perfect over a few hundred. Precision stage: order them right.

What the new pieces do

Ranking Modelservice
A heavy model that scores each candidate’s probability of engagement using rich user/item features.

Step 5 · Feed the model

The feature store

Ranking needs features — "how many cooking videos did this user watch today?", "this item’s 1-hour click-rate". Some are slow to compute; all must be fresh and identical between training and serving. Where do features come from?

Ranking Modelpredict engageFeature Storeonline + offline
New in this step: Feature Store.

Ranking needs fresh features, and training must see the SAME values. How?

  1. Heavy aggregations can’t be computed within the latency budget per request. And ad-hoc computation drifts from what training saw.

  2. Compute features in an offline pipeline AND serve them online with low latency, from one definition — so training and serving see identical values (no skew).

  3. Features are dynamic (today’s watch count); they can’t be frozen into weights. They must be served live and refreshed.

A Feature Store serves ranking its features and has two halves that must agree: an offline pipeline computes features in batch (for training), and an online store serves the freshest values at request time (for serving) — from a single definition, so both see the same numbers.

What the new pieces do

Feature Storestore
Serves the user/item/context features ranking needs, with offline (batch) and online (low-latency) halves.

Back of the envelope

offline half
batch-compute features → training data
online half
serve freshest values at request, low latency
one definition
same logic both sides → no train/serve skew
freshness matters
today’s behavior must reach ranking today

Step 6 · The last mile

Filters, rules and diversity

Ranking gives a perfectly-ordered list — but the top items might be things the user already saw, items that violate policy, or ten near-identical videos. A great score isn’t a shippable feed. What sits between ranking and the screen?

Recs GatewayorchestratesRanking Modelpredict engageFilters & Rulesdedupe · policy
New in this step: Filters & Rules.

The ranked list is perfect by score. Why isn’t it ready to ship?

  1. Top-by-score can repeat already-seen items, surface blocked content, and stack near-duplicates. Real feeds need post-ranking rules.

  2. Diversity matters, but blindly maximizing it tanks relevance. It’s one constraint among several applied after ranking, not a replacement for it.

  3. A filtering stage removes already-seen/blocked items, enforces policy and freshness, and spreads diversity — turning a scored list into a shippable feed.

A Filters & Rules stage takes the ranked list and makes it shippable: drop already-seen and blocked/policy-violating items, enforce diversity (don’t show ten near-identical items), and apply business rules (freshness, sponsored slots, fairness). Then the final feed goes to the user.

What the new pieces do

Filters & Rulesservice
Removes already-seen, blocked, or policy-violating items and applies diversity/business constraints.

Step 7 · Close the loop

Log engagement, retrain, repeat

The feed shipped. The user clicks some things, ignores others. That reaction is the single most valuable signal you have — it’s both the truth about what’s good and the fuel for tomorrow’s models. How do you use it?

Engagement LogTraining Pipeline
New in this step: Engagement Log, Training Pipeline. · swipe to pan the diagram

The user reacts to the feed. What do you do with that signal?

  1. Ignoring engagement freezes the system — it never learns the user’s shifting taste. The feedback loop is what keeps recs alive.

  2. Engagement events become training data and freshness signals: retrain the models periodically and refresh features so the system keeps improving.

  3. Without impressions you can’t tell "shown and ignored" from "never shown" — you lose the negatives the model needs to learn from.

Every impression and interaction is logged to the Engagement Log. A Training Pipeline periodically retrains the retrieval and ranking models on that data and refreshes the feature store — so the system learns from what worked and adapts as taste shifts. The loop is the product.

What the new pieces do

Engagement Logbus
Every impression and interaction, logged — the training data and freshness signal for the whole system.
Training Pipelinebus
Periodically retrains the retrieval and ranking models on logged engagement so they keep improving.

The payoff

You built a recommender

From "best ten of millions" to a self-improving funnel: two-tower retrieval, a heavy ranker, a consistent feature store, post-ranking rules, and a feedback loop that retrains on engagement.

UserRecs GatewayCandidate GenRanking ModelFilters & RulesItem IndexFeature StoreEngagement LogTraining Pipeline
The finished design, end to end. · swipe to pan the diagram

Now stale the feature store and watch relevance quietly rot — recommendations built on yesterday’s signals — and see why offline/online feature consistency is the system’s spine, not a detail.

Everything you assembled, in order

  • Retrieve → rank — cheap-wide recall, then expensive-narrow precision
  • Embeddings — taste as geometry — similarity becomes distance
  • Two-tower + ANN — live user vector × offline item index, in ms
  • Ranking Model — score P(engage) over hundreds with rich features
  • Feature Store — offline + online, consistent — no train/serve skew
  • Filters & Rules — dedupe, policy, diversity → a shippable feed
  • Feedback loop — log engagement, retrain, refresh — the flywheel
  • Freshness — stale features rot relevance with no error at all

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. A brand-new user opens the app with zero engagement history. What does two-tower retrieval actually do for them?

    There’s no learned user vector yet, so the system falls back to onboarding signals (explicit taste picks at signup, demographic/context priors) or simply popularity/trending candidates for the first session — the two-tower model only becomes meaningfully personalized once there’s enough engagement to compute a real user embedding. Cold-start users are usually served a DIFFERENT candidate-generation path entirely, not a degraded version of the personalized one.

  2. A brand-new item is uploaded with zero engagement. How does it ever get recommended if ranking is trained on historical engagement?

    Content-based signals (the item’s own embedding from its title/description/category, computed without any engagement data) let it enter candidate generation via similarity to items users already liked, and the exploration mechanism from the chaos scenario deliberately gives it some impressions despite having no track record — without either, a new item is invisible by construction, since a P(engage) ranking model trained on engagement history has nothing to score a zero-history item against.

  3. "Add some diversity" — concretely, how does a diversity rule get compared against a relevance score to decide the final order?

    A common approach is a penalty term subtracted from an item’s score based on similarity to items already placed higher in the list (maximal marginal relevance) — so the 3rd cooking video in a row takes an increasing diversity penalty even if its raw engagement score is high, until a different category’s next-best item overtakes it. It’s a tunable tradeoff (how much penalty per repeat), not a hard cap on category count.

  4. "Today’s watch count" needs to be fresh — how fresh is fresh in practice? Hourly batch, or true streaming?

    It depends on the feature’s decay rate: a feature like "watched in the last 5 minutes" (signals someone is mid-session on a topic right now) needs streaming updates to be useful at all, while "total watches this month" barely changes hour to hour and is fine on a batch cadence. The feature store’s batch+streaming split (mirrored in the feature-store system design) lets each feature pick the cadence its own volatility actually demands, rather than forcing one global freshness SLA.

  5. The ranking model predicts P(engage). How does a single score balance engagement against revenue and long-term retention, which can conflict?

    It usually doesn’t stay single — production rankers often predict multiple objectives (P(click), P(watch-to-completion), predicted revenue) and combine them with tunable weights into one final score, rather than training one model to secretly balance everything. This makes the tradeoff a product decision exposed as a weight you can dial (favor engagement this quarter, favor retention next), not a fixed thing baked irreversibly into training.

Check yourself — the answers, and why

Eight steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Recommenders use a two-stage funnel because…

    • It’s easier to code
    • No single model is both fast over millions and precise per item
    • It saves storage

    Cheap retrieval narrows millions to hundreds; a heavy ranker orders those hundreds precisely. Each stage is sized for its job.

  2. In a two-tower model, item vectors are…

    • Computed live per request
    • Precomputed offline and indexed for ANN
    • Hand-labeled

    The item tower runs offline over millions and is cached in an ANN index; only the user vector is computed at request time.

  3. The feature store’s offline and online halves must agree to avoid…

    • Slow training
    • Train/serve skew — scoring on values the model never learned from
    • Large indexes

    If serving computes a feature differently than training did, the model scores on unfamiliar values and quality silently degrades.

  4. Why log impressions, not just clicks?

    • To save space
    • Without impressions you can’t distinguish "shown and ignored" from "never shown"
    • Clicks are enough

    Negatives (shown-and-ignored) are essential training signal; impressions provide them.

  5. Training only on engagement with items the system already recommended causes…

    • Faster training
    • A self-reinforcing popularity bubble — never-shown items get no chance to prove themselves
    • More diverse recommendations over time

    Without deliberate exploration and off-policy correction, the model only ever learns from its own biased serving policy, and the effective catalog it recommends from quietly narrows.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Best few of millions: return a personalized feed from a catalog of millions in tens of milliseconds.
  • Two-stage funnel: cheap candidate generation narrows millions to hundreds, a heavy ranker orders those precisely.
  • Represent taste: learn user and item embeddings so similarity becomes distance in a shared space.
  • Score & filter: a heavy ranking model predicts engagement, then filters dedupe seen/blocked and add diversity.
  • Close the loop: log every impression and interaction, retrain the models, and refresh features.

The qualities that shape everything

Each one names the mechanism that buys it.

Both fast over millions and precise per item
A two-stage funnel: cheap retrieval narrows millions to hundreds, then a heavy ranker orders those hundreds — each stage sized for its job.
Retrieve candidates in milliseconds
A two-tower model: item vectors precomputed offline and indexed, the user vector computed live, meeting via an ANN search at request time.
Order candidates by real engagement
A heavy ranking model scores each candidate’s P(engage) using rich user/item/context features — affordable over hundreds, not millions.
No train/serve skew
A feature store with offline (batch) and online (low-latency) halves computed from one definition, so training and serving see identical values.
A scored list becomes a shippable feed
A filters & rules stage drops already-seen and blocked items, enforces diversity, and applies business rules after ranking.
Keep improving as taste shifts
Log every impression and interaction and retrain retrieval + ranking on it, refreshing the feature store — the flywheel.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

A two-stage funnel over scoring every item with the best model

Running a heavy model over millions per request is impossible in the latency budget — you shrink the set cheaply first, then rank precisely.

Learned embeddings over matching on shared tags/categories

Tags are coarse and miss latent taste (two thrillers can feel totally different); learned embeddings place similar users and items near each other so similarity is distance.

Two-tower + ANN over a dot-product against every item vector

Exact scoring over millions per request blows the budget; precompute item vectors offline and run an ANN index to pull the nearest few hundred in milliseconds.

A feature store (offline + online) over recomputing features inline per request

Heavy aggregations can’t be computed in the latency budget and ad-hoc computation drifts from what training saw — one definition serving both sides prevents train/serve skew.

Post-ranking filters and rules over returning the top-by-score list

A top-by-score list can repeat already-seen items, surface blocked content, and stack near-duplicates; post-ranking rules encode the product judgment a score can’t.

What this teaches

Learn AI system design by building a large-scale recommendation system step by step. An interactive guide covering the two-stage retrieve-and-rank funnel, two-tower embedding retrieval with ANN, a heavy ranking model, the feature store and its offline/online split, filtering business rules, and the feedback loop that keeps recommendations fresh.

Key takeaways

  • Retrieve → rank — cheap-wide recall, then expensive-narrow precision
  • Embeddings — taste as geometry — similarity becomes distance
  • Two-tower + ANN — live user vector × offline item index, in ms
  • Ranking Model — score P(engage) over hundreds with rich features
  • Feature Store — offline + online, consistent — no train/serve skew
  • Filters & Rules — dedupe, policy, diversity → a shippable feed
  • Feedback loop — log engagement, retrain, refresh — the flywheel
  • Freshness — stale features rot relevance with no error at all

Concepts covered

  • Pick the best few from millions
  • A request and a funnel
  • Embeddings for users and items
  • Two-tower retrieval with ANN
  • The heavy ranking model
  • The feature store
  • Filters, rules and diversity
  • Log engagement, retrain, repeat
RUN IT YOURSELF

Item-based collaborative filtering

"Because you liked X" works by finding items similar to ones you already liked — item-item cosine similarity over a rating/feature matrix. Here it is in real Python, running live. Read the comments, edit the catalog, and hit Run.

HOW TO READ THE CODE — 4 IDEAS
  1. Each item is a feature vector (here: how action / comedy / romance it is).
  2. Two items are similar if their vectors point the same way — cosine similarity.
  3. Score each unseen item by its best similarity to something you liked (steps 1–2).
  4. Recommend the top-k, never re-suggesting what you have seen (step 3).
CPython · WebAssembly
built to be reasoned about, not memorized — make the calls, stale the features, run the quiz.
Finished this one? 0 / 61 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs