ML Systems Lab Open interactive version →
Foundational 30 min read RecSyscandidate generationrankinglatency budget

The Two-Stage Architecture

Why candidate generation → ranking exists — latency forces a cheap recall stage before an expensive precision stage

Every serious recommender answers one question: "of the millions of things I could show this user, which handful do I show *now*?" The naive answer — score every item with your best model and take the top-k — is arithmetically impossible, and understanding *why* is the whole foundation of RecSys design.


The latency wall is the forcing function. Suppose a good ranker takes 1ms to score one (user, item) pair — generous, since real cross-feature rankers are slower. A catalog of 10M items would need 10,000,000 ms = 10,000 seconds of compute per request. Your latency budget is ~100ms end-to-end. You are off by five orders of magnitude. No amount of hardware closes a 100,000× gap at request time; you cannot buy your way out of an asymptotic mismatch.


So the system splits into two stages with opposite objectives. *Candidate generation* (retrieval) cheaply narrows 10M → a few hundred using methods so cheap they can touch every item — embedding lookups, approximate nearest neighbor, precomputed lists. It optimizes recall: don't lose the good items. *Ranking* then runs an expensive, feature-rich model over only those few hundred survivors and optimizes precision: order them exactly right. The cheap stage runs over everything; the expensive stage runs only over what the cheap stage kept.


The consequence that trips up juniors: the two stages have different metrics because they have different jobs. Retrieval is judged on recall@k (did the relevant items make the shortlist?), ranking on NDCG/precision (are they ordered well?). And retrieval's recall is a *ceiling* — an item retrieval drops is gone forever; no ranker can order an item it never received. A brilliant ranker on top of a mediocre retriever is capped by the retriever. This is why "great model, mediocre recommendations" almost always means: audit retrieval recall first.


That answers *which stage broke* — it doesn't answer *how many candidates stage 1 should even keep*. Keep too few and precision-optimized ranking never sees the items that mattered; keep too many and the ranking stage — the expensive one — blows its own slice of the 100ms budget. The split point is a real design decision, and the same arithmetic that motivated the two stages in the first place answers it.

Say the team picks 500 candidates to pass forward — the number this module's own check questions already assume. The ranking stage gets roughly 20ms of the 100ms total (retrieval takes ~2ms; the rest goes to serialization, logging, and network). 20ms ÷ 500 candidates = 0.04ms per candidate — about 25× cheaper, per item, than the naive 1ms-per-pair full ranker from the latency wall above. That's the actual constraint on the ranking model: it has to be far cheaper than the heaviest model you could build, because it still runs 500 times inside one request. This is why production rankers are feature-rich but *bounded* — a few hundred features, not deep cross-attention over raw text — the per-item cost has to clear 0.04ms, not 1ms.

Now the second half of the split-point decision: what does retrieval's recall actually cost you downstream? Assume, for a given request, the truly relevant items a perfect system would show in the top-10 number 10. If retrieval's recall@500 is 0.90, only 10 × 0.90 = 9 of those 10 ideal items survive into the 500 candidates the ranker ever sees — the ranker, however good, can place at most 9 of the true top-10 correctly, a 90% ceiling on top-10 precision that no ranking sophistication can lift. Push recall@500 to 0.98, and 10 × 0.98 = 9.8 ≈ 10 of the ideal items survive — the ceiling moves to essentially 98-100%. The gap between a "good" retriever (0.90) and a "great" one (0.98) is worth roughly 8-9 points of achievable top-10 precision, bought entirely by retrieval and unrecoverable by ranking — which is exactly the earlier claim, "retrieval recall is a ceiling," made numeric.

Key points

Takeaway

A recommender is a recall-then-precision funnel forced by a ~100,000× latency gap: cheap candidate generation maximizes recall over millions (setting an unraiseable ceiling on final quality), then an expensive ranker maximizes precision over the few hundred survivors. The split point itself is arithmetic, not guesswork: candidate count sets the ranking stage's per-item cost ceiling, and retrieval's recall sets the top-10 precision ceiling — one model cannot occupy both ends of the funnel, and no ranking improvement buys back recall retrieval already lost.

Recap

Check your understanding

Q1. An interviewer asks you to "design YouTube recommendations." You have a strong ranking model. What is the correct *first* architectural move?

Q2. Your ranker scores 0.94 AUC offline, but users say the feed misses obvious interests. Retrieval recall@500 is measured at 0.6. Where is the bug?

Q3. Select the *two* legitimate reasons candidate generation can't simply be "the full ranking model over a random 1% sample of the catalog."

Q4. Team A improves retrieval recall@500 from 0.90 to 0.98 and changes nothing about the ranker. Team B leaves recall@500 at 0.90 and ships a much better ranking model. Assuming a true top-10 of 10 relevant items per request, which team's change can actually raise achievable top-10 precision, and by roughly how much?

Try it interactively

ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.

Open ML Systems Lab →