← Back to all questions
AI System DesignStaffVisual EmbeddingsANN Retrieval

Design a Visual Search System (Image-as-Query Retrieval)

A visual search system retrieves visually similar or shoppable items from a billion-image corpus given a user photo as the query. Unlike text product search or generic ANN-over-precomputed-vectors, here the embedding is produced live from the query image and the system must localize the object, crop it, encode it, and retrieve under a sub-second mobile budget. Companies like Pinterest (Lens), Google Lens, Amazon, and Airbnb build exactly this and interview AI/ML and SDE candidates on it.

Level
Staff
Category
AI System Design
Interview time
60 min
100% free · No login required
WHAT THIS QUESTION TESTS
·Can you justify on-device vs server inference for the visual encoder with a concrete latency/cost/freshness tradeoff?
·Do you localize and crop the object before embedding, and explain why whole-image embedding fails for shoppable search?
·Can you pick an ANN index (HNSW vs IVF-PQ) for billion-scale and defend the recall/memory/latency tradeoff?
·Do you train the embedding with metric learning + hard-negative mining and evaluate visual relevance, not just classification accuracy?
★ STAFF-LEVEL SIGNALS
Frames a per-stage latency budget that sums to the sub-second target and names where each ms goes
Treats the image→product mapping as a distinct cross-modal alignment problem, not a free byproduct of the encoder
Designs index refresh/freshness (incremental upserts vs periodic rebuild) against a real catalog-churn rate
Defines the offline/online eval loop — relevance human labels, embedding recall@k, and a guardrail metric — before scaling
0

Scope — Frame the problem & who's asking

The query is an image and the embedding is produced live — that one fact reshapes the entire design vs text search.

In text product search you retrieve over precomputed document vectors using a cheap query embedding (a few tokens). Here the query is a photo, the query vector is produced by running a real visual encoder on the critical path, and before you can even embed it you have to find the object in the frame. That inverts where the cost and the risk live. Spend the first minutes naming that, not drawing boxes.

The one-liner

A user points their camera at a couch (or a jacket, or a lamp), and you return visually similar — or buyable — items from a billion-image corpus, sub-second, on a phone. The load-bearing parts are the live visual encoder, object localization + crop, and the image-to-product mapping. Everything else (the ANN index, sharding, caching) is well-trodden distributed systems.

Three different questions

Who is asking shapes what “good” looks like. State this explicitly so you answer the right interview.

Who
What they probe
Where you must be strong
SDE
On-device vs server inference, index sharding/refresh, sub-second mobile p99, dedup/diversity
Per-stage latency budget, index freshness, RAM-cost math
MLE
Unified multi-task embedding, metric learning + hard-negative mining, detect/crop, image-to-product, visual-relevance eval
Loss matches the retrieval task; eval separates similar vs same-product
Switcher (SDE to AI)
Anchor on retrieval/sharding/latency systems half; must explain WHY the encoder runs live and WHAT the loss optimizes
Connect ANN/sharding fluency to the one AI fact: the embedding is learned and produced live

A switcher’s winning move: own the systems half confidently, then show you understand the encoder runs live because the query has no precomputed vector, and the loss optimizes distance in embedding space, not classification accuracy.

Three retrieval targets

Conflating these is a junior tell. They share a backbone but have different positives, different rerankers, and different evals.

Target
User intent
Graded on
Visually similar ("more like this")
Inspiration, browse
recall@k vs human similarity labels
Exact-same-product
Find this exact item
same-product precision@1 (the SKU)
Shoppable (lifestyle to catalog)
Buy something like what I see
cross-modal precision, conversion

“Shoppable” is the hard one: a messy in-the-wild photo must map to a clean studio catalog shot. That is cross-modal alignment, covered in Step 4.

Scope & numbers

Dimension
Target
Corpus size
~1B+ images, petabytes of raw visual data
Query volume
Millions of queries/day
Embedding dim
256–1024, L2-normalized
Latency
Sub-second end-to-end on mobile; target p99 under 700ms
Index recall
recall@k above ~90% at the candidate stage

Non-goals (name and defer): full multimodal VQA (“what is this and is it dishwasher-safe”), OCR / text-in-image extraction, and AR overlay. All real, all separate systems. Cutting them early shows scoping discipline.

1

Requirements — End-to-end flow & latency budget

The pipeline

Two paths share one model but run at different times and scales.

QUERY PATH (online, one image, live)
capture --> [on-device detect/crop?] --> upload crop
--> server encode --> ANN retrieve top-1000
--> exact rerank top-100 --> dedup + MMR
--> return top-k
 
INDEX/BUILD PATH (offline, whole corpus, batch)
catalog images --> same encoder (batch GPU)
--> vectors --> quantize/PQ --> build/upsert index
--> shard + replicate

Index/build path vs query path

This is a common miss: candidates draw one pipeline and embed everything live. The corpus is embedded once (plus incremental upserts as the catalog churns); only the single query image is embedded live. The critical, latency-sensitive code is the query path. The expensive, throughput-sensitive code is the build path. They must, however, use the same model version — the subtle coupling we defend in Step 6.

Where the milliseconds go

Budget every stage so the sum sits under target with headroom. Numbers are p99 estimates.

Stage
Budget (p99)
Notes
Capture + transport (upload)
~150ms
Upload a crop, not a 12MP photo
Detect + crop
30–60ms
On-device single-stage detector
Encode
20–40ms server GPU; ~28ms quantized MobileNetV2 on ARM CPU
The live query vector
ANN retrieve (top-1000)
20–50ms
Coarse search on compressed codes
Exact rerank (top-100)
~30ms
Float distance, recover precision
Dedup + MMR diversity
~10ms
Collapse near-dups, diversify
Network return
~100ms
Results payload to device
Total
~700ms
Leaves margin for tail

Upload tradeoff

Sending the full 12MP photo can cost seconds on mobile networks. Sending a 224–384px crop of the detected object cuts transport to ~150ms, shrinks server decode cost, and strips the PII-laden background (the room, faces, address). Detect-and-crop on-device pays for itself on transport alone.

Coarse-to-fine

ANN returns ~1000 candidates over a compressed/binary code (fast, approximate). Then an exact float rerank on the top ~100 recovers the precision that quantization threw away. This two-stage shape recurs in Steps 5 and 7 and is how you get both speed and accuracy.

2

Estimation — Query understanding: detect & crop

Why crop first

This is the load-bearing fix. A whole-image embedding of “a couch in a living room” encodes the room — the rug, the window, the wall color — and the couch signal is diluted to a fraction of the vector. Retrieval then returns living rooms, not couches. Localizing the salient object and embedding only the crop concentrates the signal on the thing the user actually pointed at.

Detector choice

Approach
Strength
Weakness
Fit
Single-stage (YOLO family)
Fast, real-time; YOLOv10-S is NMS-free, ~1.8x faster than RT-DETR-R18 at similar accuracy
Slightly lower peak accuracy
Mobile / on-device crop
Two-stage (Faster R-CNN)
Higher localization accuracy
Slower, heavier
Server-side, accuracy-critical
Taxonomy-decoupled / classification-free
Proposes regions without a fixed class set
Needs downstream signal to label
New categories without retraining

Taxonomy-decoupled region proposals are the staff move. A classification-free detector proposes object regions without committing to a fixed class taxonomy, so adding a new product category (a new furniture type, a new apparel cut) does not force a detector retrain. Pinterest-style weak labeling derives category signal across hundreds of categories by aggregating similar-result behavior rather than hand-labeling boxes.

Multi-object & fallbacks

Multi-object scenes: a single photo often has several shoppable objects (couch, lamp, rug, art). Detect all salient objects, render tappable bounding boxes, and let the user pick which one to search — Pinterest’s automatic object detection in visual search works this way. Without this, you guess wrong and the user thinks the product is broken.

Fallback: if no detection clears the confidence threshold, do not fail — embed the whole image via a center-crop and return best-effort results. Graceful degradation beats a zero-result screen. The crop-vs-recall tradeoff: a tight crop sharpens precision but can clip context the encoder needs; a looser crop or whole-image fallback trades precision for never returning empty.

3

API design — The visual encoder & embedding training

One backbone, many heads

The encoder is the system’s API surface: image in, fixed-dim L2-normalized vector out. The staff design is a unified multi-task embedding — ONE backbone feeding multiple task heads (similarity, category, attributes) — so a single vector serves all three retrieval targets (similar-pin, same-product, shoppable). This is the Pinterest PinCLIP / SearchSAGE pattern: visual-semantic coherence learned from co-save signals, plus engagement signal from query-click.

Component
Choice
Backbone (server)
ViT, or efficient hybrid (RepViT / EfficientFormer)
Backbone (on-device)
MobileNetV3-L (~1ms on iPhone ANE)
Output
Fixed 256–1024-dim, L2-normalized
Heads
Similarity (metric) + category + attributes

The loss

Train with metric learning, not classification. A softmax-classification head optimizes “is this a chair,” but retrieval is graded on distance: anchor close to positive, far from negative. Use a contrastive / triplet loss or an ArcFace-style angular margin.

for batch in loader:
anchor, positive = sample_pair(batch) # co-save / same-SKU
negatives = hard_negative_index.query( # closest wrong items
anchor, k=N)
z_a = normalize(encoder(anchor))
z_p = normalize(encoder(positive))
z_n = normalize(encoder(negatives))
# InfoNCE: pull positive, push the N hard negatives
loss = info_nce(z_a, z_p, z_n, temperature=0.07)
loss.backward(); opt.step()
if step % refresh == 0:
hard_negative_index.rebuild(encoder) # refresh negatives

Hard negatives

Random negatives are too easy — most random catalog items are obviously unrelated, so gradients vanish early. Hard-negative mining samples the closest wrong items in embedding space (within-batch, or from a periodically-refreshed index of corpus embeddings). These are the cases the model is about to get wrong, so they drive faster, sharper convergence and tighten the decision boundary where it matters.

Pretraining

Representation quality at this scale comes mostly from large weakly-supervised pretraining: more than 1B images with weak labels — co-saves, click-throughs, catalog metadata — before any fine-tuning on curated relevance data. Weak labels are noisy but vastly abundant, and at a billion images that scale dominates the quality of the learned space. Fine-tune on smaller human-labeled pools afterward.

4

Data model — Image-to-product cross-modal mapping

The domain gap

The catalog side is the data model for shoppable search, and it lives in a different domain than the query. A user’s photo is messy: ambient lighting, an oblique angle, a cluttered background, partial occlusion (a bag half behind a chair). The catalog image is a clean studio shot: white background, even light, canonical angle. Matching one to the other is cross-modal / cross-domain alignment, not plain nearest-neighbor in a single domain. Treating it as a free byproduct of the encoder is the trap that sinks otherwise-good designs.

Aligning the two sides

Approach
How it works
When
Shared embedding space
Train on (query-photo, catalog-photo) positive pairs so both land together
You have paired data
Learned projection
Map the query-side vector into the catalog-side space
Asymmetric encoders / retrofit
Vision-language alignment
Align fine visual detail to text attributes (CLIP-style); meaningful gains over an image-only baseline
Rich catalog text/attributes

A CLIP-style joint image-text space lets you fuse the crop embedding with catalog text and attributes. Reported evidence that multi-modal product representations beat image-only: CLIP-ITA (image + text + attributes) showed up to ~+265% R-precision over a text-only BM25/MPNet baseline, and ~+59% R-precision over an image-only CLIP-I baseline, on e-commerce category-to-image retrieval. The lesson: catalog text and attributes are signal, not decoration.

The non-negotiable: the catalog side is embedded offline and indexed, the query side is embedded live, and they MUST land in the same space. That is why joint or paired training matters — train them apart and the live query vector simply will not be near its catalog match.

Evaluating same-product

Grade same-product precision@1 separately from visual-similarity recall@k. A returned item can look strikingly similar — same silhouette, same color — yet be the wrong SKU (different brand, different price, knockoff). Visual-similarity metrics happily reward that; shoppable search is graded on the SKU. Two distinct eval pools, reported as two distinct numbers, so a similar-looking-but-wrong-product regression cannot hide behind a healthy recall number.

5

High-level architecture — ANN retrieval & index at billion scale

Index choice

Index
Recall / latency
Memory
Best for
HNSW
Excellent recall, low latency
Whole graph in RAM — expensive at 1B
Latency- and recall-critical serving
IVF-PQ
Tunable via nprobe; >90% recall at ~1% clusters searched
Compressed, fits billion-scale cheaply
Memory-bound billion-scale
IVF-Flat
High recall, no PQ loss
Larger than PQ, smaller than HNSW
Mid-scale, accuracy-sensitive

HNSW gives the best recall-latency curve but the entire navigable-graph lives in RAM, which is costly at a billion vectors; it is what you reach for when latency and recall dominate and RAM is affordable (Pinterest’s Manas serving is HNSW-based). IVF-PQ uses inverted-file clustering plus product quantization to compress vectors so a billion fit in far less memory; tune nprobe to trade recall for latency — a typical config searches ~1% of clusters (e.g., 10 of 1000) and still holds above 90% recall.

Rule of thumb: HNSW when data churns, recall matters, and RAM is affordable; IVF-(PQ) past ~50M vectors once memory is the binding constraint. A hybrid IVF-HNSW (HNSW over the coarse centroids) is common at the very top of scale.

Sharding & refresh

Partition the 1B corpus across nodes — by hash for even load, or by category for routability and cheaper category-scoped queries. Query shards in parallel, merge the per-shard top-k. Replicate each shard for QPS headroom and availability. (Freshness/refresh of the index is its own staff topic, defended in Step 6.)

Compression

PQ / binary codes shrink a 512-d float vector from ~2KB to tens of bytes — the difference between a billion-vector index that fits in RAM and one that does not. The cost is approximation error, which you recover with an exact float rerank on the top candidates (the coarse-to-fine pattern from Step 1). Compress for the recall stage, go exact for the precision stage.

6

Deep dive — Serving, freshness, latency & cost

WHERE STAFF IS WON

This is where the level shows. Senior gives “client/server split, cache, roughly 800ms.” Staff defends a budget, the model-version coupling, freshness against churn, and the cost reckoning.

On-device vs server

Dimension
On-device encoder
Server-side encoder
Latency
No network RTT for encode
Adds upload + RTT
Model
MobileNetV3-L ~1ms ANE; quantized MobileNetV2 ~28ms ARM CPU
Full ViT, most accurate
Freshness
Pinned — new model needs an app release
Ship a new model anytime
Privacy
Image never leaves device
Image uploaded
Cost
Free compute (user's phone)
GPU fleet cost

Staff answer: detect/crop on-device (cheap, kills the transport tax, strips PII), encode server-side with the full corpus-matched model so quality and freshness stay high. Reserve on-device encode for privacy-sensitive or offline modes. The decision is not ideological — it follows from one constraint below.

The versioning trap

The query embedding and the corpus embedding must come from the same model version. They live in the same vector space only if produced by the same weights. So:

  • An on-device encoder pins you: you cannot ship a new embedding model without an app release, and the index side must hold at that same version until the app fleet updates.
  • A model rollout means re-embedding the corpus (a billion forward passes) or running dual indexes — old and new — during migration, routing each query to the index matching its encoder, then cutting over.

This is the subtle staff point and the one that separates levels: the encoder and the index are ONE versioned unit. A “small model improvement” is never small — it invalidates a billion vectors.

Index freshness

Catalogs churn constantly: new SKUs, price changes, stock-outs. Design refresh against a real churn rate, not as an afterthought.

  • Incremental upserts for the hot catalog — modern vector DBs make a committed vector searchable within seconds, so a new SKU is findable almost immediately.
  • Periodic full rebuild to restore graph quality (HNSW graphs degrade under heavy incremental churn).
  • Set a freshness SLA: e.g., a new SKU is searchable within minutes; an out-of-stock item is suppressed within seconds.

Defending the budget

under 700ms p99 = transport 150 + detect/crop ~50 + encode ~30 + ANN ~40 + rerank ~30 + dedup/MMR ~10 + return ~100, with headroom left over. Then name the tail risks and mitigations:

  • Cold shard (just-loaded replica, empty cache) — keep warm replicas, pre-touch.
  • GPU queueing at the encoder — batch with a tight max-wait, autoscale on queue depth.
  • Tail-latency shards dominating the parallel merge — hedge requests, drop a slow shard past a deadline and serve partial top-k.

Cost

The dominant cost is billion-scale HNSW in RAM. The staff cost move is tiering: serve hot, high-traffic categories from HNSW in RAM for best recall/latency, and push the long tail to IVF-PQ on disk/SSD where memory is cheap and a little extra latency is acceptable. Compression (PQ/binary) plus tiering is what makes a billion-vector index economically sane, and it is exactly the lever a senior answer skips.

7

Rollout strategy — Dedup, diversity & ranking

Kill the duplicates

Catalogs are full of near-identical images: the same SKU sold by multiple sellers, the same product shot from several angles. Without intervention the user sees “8 of the same item.” Collapse near-duplicates by an embedding-distance threshold before display, keeping one canonical representative per cluster. This runs in the dedup/MMR ~10ms budget from Step 1.

Diversify

Even after dedup, the raw top-k clusters tightly around the single closest match. Apply MMR (Maximal Marginal Relevance) to balance relevance against inter-result diversity with a tunable lambda (0 = pure relevance, 1 = pure diversity). Tuned right, results span colors, styles, and price points instead of ten variants of one chair — which is what makes the result grid feel useful rather than redundant.

Rerank with business signals

Visual similarity gets you candidates; business signals order them. On the top ~100, run a lightweight ranker that blends the visual score with availability, price, seller quality, and engagement (save/click/purchase rate). An out-of-stock perfect match should not outrank an in-stock near-match.

Personalization (optional): mix user history/affinity in as a reranking feature only — keep it out of the ANN recall stage so the index stays query-only and shareable across users (per-user indexes do not scale to a billion vectors).

Result count + paging: return ~20–50 results with infinite scroll; relax the diversity constraint on later pages so deep scrollers can drill into a specific look.

8

Bottlenecks, observability & evolution — Evaluation, monitoring & iteration

Offline metrics

Metric
Measures
Target/use
recall@k
Did the right items make the candidate set
Candidate-stage health
mAP
Ranking quality across the pool
Overall relevance
same-product precision@1
Right SKU at rank 1
Shoppable, graded separately
Golden eval set per category
Stable regression baseline
Catch per-category drops

Build human-labeled relevance pools and a per-category golden set so a model or index change is graded the same way every time.

Online metrics

Metric
Type
CTR on results, saves
Engagement
Add-to-cart / purchase (shoppable)
Conversion
Latency p99, zero-result rate, dedup rate
Guardrails

A model that lifts offline recall but raises p99 or zero-result rate is not shippable — guardrails gate the launch.

Drift & A/B

Human-in-the-loop: side-by-side relevance judgments calibrate the offline pool and catch “looks similar but wrong category” failures the model is structurally blind to.

Drift monitoring: track query-distribution shift (new product trends, seasonal demand), embedding-space drift, and recall regression after every index rebuild or model rollout — the two operations most likely to silently break retrieval.

Iteration: ship encoder and index changes behind A/B with interleaving (more sensitive than split traffic for ranking), and close the loop — feed production misses (low-engagement queries, human-flagged bad results) back in as hard negatives for the next training round. The mining loop in Step 3 is fed by production, not just static data.

Summary

The three things that matter

What makes this not text product search or generic ANN-over-precomputed-vectors, and what to name in the first two minutes:

1. Live query embedding — the query has no precomputed vector; the encoder runs on the critical path.

2. Object localization + crop — embed the object, not the room.

3. Cross-modal image-to-product mapping — lifestyle photo to clean catalog shot is alignment, not similarity.

Senior vs Staff

Senior stops at “detect to embed to ANN to top-k.” Staff defends a per-stage latency budget that sums to target, the model-version coupling between query and corpus embeddings, index freshness against catalog churn, and separate evals for visually-similar vs same-product. The cross-cutting staff move: treat the encoder and the index as ONE versioned unit, and treat retrieval quality as something you measure (recall@k + human relevance + online conversion) before you scale it.

Common traps

  • Embedding the whole image instead of the crop.
  • Assuming image-to-product is free rather than a distinct alignment + eval problem.
  • Picking HNSW at 1B without the RAM-cost reckoning (tiering / IVF-PQ).
  • Forgetting a model rollout invalidates the whole index (re-embed or dual-index).
  • Returning 8 near-duplicates (no dedup, no MMR).

If you only say one thing: the embedding is produced live from a cropped object, and the query encoder and the corpus encoder must be the same versioned model — everything else follows from that.

Rubric — Senior vs Staff

Dimension
Senior signal
Staff signal
Problem framing & scoping
Clarifies whether it's similar-pins vs shoppable, fixes a corpus size and QPS, and states the sub-second goal.
Names the distinct hard part (live query embedding + localization + image→product) up front and scopes around it; separates 'similar' from 'same product' from 'shoppable' as different retrieval targets with different evals.
Query understanding (detect + crop)
Adds an object detector and crops the salient region before embedding; knows whole-image embedding dilutes the signal.
Uses a taxonomy-decoupled / classification-free detector so new categories don't require retraining; handles multi-object scenes with tap-to-select and a fallback whole-image embedding; reasons about crop-vs-recall tradeoffs.
Embedding model & training
Picks a CNN/ViT encoder, trains with triplet/contrastive loss, produces a fixed-dim vector.
Designs a unified multi-task embedding (one backbone, multiple heads), trains with metric learning + online hard-negative mining, uses large weakly-supervised pretraining, and binarizes/quantizes for serving without tanking recall.
Retrieval & ANN at scale
Chooses an ANN library and an index (HNSW or IVF), shards by corpus, returns top-k.
Defends HNSW (recall/latency, RAM cost) vs IVF-PQ (memory, billion-scale) with concrete recall/nprobe numbers; designs sharding + replication, a coarse-to-fine two-stage retrieve, and an exact-rerank tail.
Image→product mapping
Maps the query embedding to nearest catalog items in the same space.
Treats image→product as cross-modal alignment (lifestyle photo → clean catalog shot), trains the catalog and query sides jointly or with a projection, and evaluates same-product precision separately from visual similarity.
Serving, latency & freshness
Splits inference client/server, caches, and gives a rough latency number.
Lays out a per-stage budget summing to <700ms p99, picks on-device vs server with cost math, and designs index refresh (incremental upserts for catalog churn vs periodic rebuild) with a freshness SLA.
Eval, dedup & iteration
Mentions recall@k and some human relevance judgment; dedups exact duplicates.
Defines offline (recall@k, mAP, human relevance) + online (engagement, save/purchase, guardrails) loop; applies near-dup collapse and MMR diversity so results aren't 8 of the same item; plans A/B and drift monitoring.
★ MORE WALKTHROUGHS

Want more breakdowns like this?

Join free early access for upcoming RAG, LLM eval, agents, and AI infrastructure walkthroughs.

Join Free Early Access →