Design problems
Search & discoveryHardSDE-3 40 min

Design a web search engine

Asked at
AmazonGoogleMicrosoft

The interviewer asks

“Design a web search engine like Google. A user types a query and gets the ten most relevant web pages in a fraction of a second. Assume the crawler already exists and hands you fetched pages.”

What a strong answer looks like

A strong answer splits the problem into an offline side and an online side. Offline: crawled pages flow into an indexing pipeline that parses, deduplicates, computes link-based quality signals (PageRank) and builds a sharded inverted index, partitioned by document, with batch rebuilds plus a small fresh index for news. Online: a query service rewrites the query (spelling, synonyms), scatters it to every index shard, each shard returns its top-K by a cheap score (BM25 plus static quality), a root aggregator merges, and a second-stage ranker (learning-to-rank, optionally a small neural model) re-ranks the top few hundred. Snippets come from a separate document store. Latency is held under ~200 ms by replication, tail-tolerant requests (hedging) and a result cache for the head of the query distribution. The candidate should name the scatter-gather fan-out cost, the document-vs-term partitioning trade-off and index freshness as the hard parts.

Concepts to mention

Inverted index with posting listsDocument-partitioned shardingScatter-gather with aggregator treeBM25 plus static quality signals (PageRank)Multi-stage ranking (retrieval, LTR, neural re-rank)Tail latency: hedged requests and deadlinesBatch index plus fresh real-time tierNear-duplicate detection (SimHash)Result cache for head queriesIndex versioning and safe rollout

1 Requirements

Functional

  • User submits a text query and gets the top 10 results with title, URL and snippet
  • Results are ranked by relevance to the query and page quality
  • Support pagination (page 2, 3...) for a query
  • Spelling correction and 'did you mean' suggestions
  • New and updated pages appear in results (news within minutes, the rest within days)
  • Basic filters: language and region

Non-functional

  • p99 query latency under 300 ms end to end, p50 under 100 ms at the backend
  • Availability 99.99% for the query path; a partial result is better than an error
  • Index of ~50 billion pages; serve ~100k queries/s at peak
  • Index freshness: news pages searchable within 5 minutes, general web within 1-7 days
  • Horizontal scale: adding pages or traffic means adding machines, not redesigning
  • Spam resistance: link farms and keyword stuffing should not rank

Out of scope (say it out loud)

  • The web crawler itself (fetching, politeness, URL frontier)
  • Ads auction and ad serving
  • Image, video and maps verticals
  • Typeahead / autocomplete (separate design)
  • Personalised results based on signed-in history

2 Estimates

Query QPS~60k/s average, ~100k/s peak~5B searches/day / 86,400 s ~ 58k/s; peak ~1.7x
Raw page storage~2.5 PB compressed50B pages x ~50 KB compressed HTML/text ~ 2.5 PB (keep it in a distributed doc store, replicated 3x ~ 7.5 PB)
Inverted index size~150-250 TB per full copy50B docs x ~1,000 unique terms = 5 x 10^13 postings; ~1 byte each (delta + varint doc ID and frequency) ~ 50 TB; positions for phrase queries add ~2-4x
Index shards~5,000 shards of 10M docs50B / 10M = 5,000 shards; each shard ~30-50 GB of index, fits in RAM + SSD on one machine
Serving machines~5,000 shards x ~20-40 replicas (all regions)Each query hits every shard, so every shard sees the full ~100k QPS; one replica handles ~1-2k shard-queries/s -> ~70 replicas worst case; a 40% cache hit rate and a tiered index (most queries never reach the full tier) cut that to ~20-40
Result cache hit rate~30-50%Query frequency is Zipf-like: the head (weather, cricket score, facebook login) repeats; the long tail (~15% of queries are never seen before) does not
Indexing throughput~1M pages/s across the clusterRe-index 50B pages in ~2 days ~ 50B / 172,800 s ~ 290k pages/s; budget 1M/s with headroom for re-crawls

3 API

EndpointWhat it does
GET/search?q={query}&page={n}&lang={lang}&region={cc}Return ranked results: [{url, title, snippet}], spelling suggestion, and a cursor for the next page
gRPCIndexShard.Search(terms, k, filters)Internal: one shard returns its local top-K doc IDs with first-stage scores
gRPCDocStore.GetSnippets(doc_ids, terms)Internal: fetch title, URL and a query-dependent snippet for the final 10 docs
POST/internal/index/documentsInternal: crawler/indexing pipeline pushes parsed documents for the fresh tier

4 Data model

inverted_index_shardCustom on-disk index (SSTable-style files) + RAM
term -> posting list [(doc_id, term_freq, positions[], field flags)]sorted by doc_iddelta + varint encoded

Document-partitioned: each shard owns ~10M docs and has a full term dictionary for them. Immutable segments built offline, memory-mapped; posting lists ordered so high-quality docs come first allow early termination.

doc_storeBigtable / HBase-style wide-column store
doc_id (row key)urltitlecleaned_textlanguagefetch_timecontent_hash

Used for snippets and for rebuilding the index. Row key can be reversed-domain URL so pages of one site sit together for dedup and site-level signals.

doc_signalsColumnar files on object storage, loaded into shard RAM
doc_idpagerankspam_scorefreshnessclick_through_priorlanguage

Static, query-independent scores computed in batch (Spark/MapReduce over the link graph); shipped with each index build.

link_graphDistributed file system (HDFS/S3) as adjacency lists
src_doc_id[dst_doc_id]anchor_text

Input to PageRank and anchor-text indexing. Anchor text is indexed as a field of the target page.

result_cacheMemcached/Redis cluster
key = normalised(query, lang, region, page) -> [doc_idscore]ttl

Short TTL (minutes) for news-like queries, longer for stable ones; invalidated implicitly when a new index version goes live (version in the key).

query_logsKafka -> data lake (Parquet)
queryresults shownclicksdwell_timetimestamp

Training data for spelling correction and the learning-to-rank model.

5 Architecture

CLIENTSEDGESERVICESASYNCDATAEXTERNALGET /searchcheck cachespell, rewritescatter top-Kfinal 10 docsfetched pagesarchivefresh tierdaily buildnew index versionBrowser / appGeoDNS + load balancerSearch frontendQuery understandingRoot aggregator +rankerSnippet serviceFresh indexer (stream)Crawled pages (Kafka)Index builder (batch)Result cacheIndex shards (x5,000)DFS: pages, links,signalsDocument storeWeb crawler
ClientEdge / gatewayService / workerQueue / streamCacheDatabase / storageAI / modelThird partyrequestasync (event / queue)

Hover a box to highlight its connections.

6 Request flows

Serve a query

  1. 1User sends GET /search?q=ipl+score+today; GeoDNS routes to the nearest data centre, the LB picks a frontend.
  2. 2Frontend normalises the query (lowercase, Unicode normalisation, transliteration hints) and checks the result cache keyed by (query, lang, region, page, index_version).
  3. 3On a miss, query understanding fixes spelling ('ipl scor' -> 'ipl score'), adds synonyms, detects intent (fresh/news) and returns a parsed query tree.
  4. 4The root aggregator fans the query out to one replica of every index shard (in practice through a tree of intermediate aggregators, ~50 children each).
  5. 5Each shard intersects posting lists for the terms, scores candidates with BM25 + static quality (PageRank, spam score), and returns its top ~50 doc IDs with scores within a ~50 ms deadline.
  6. 6Aggregators merge partial lists; the root takes the global top ~500 and runs the learning-to-rank model using richer features (term proximity, anchor text match, click priors, freshness).
  7. 7The frontend asks the snippet service for the final 10 docs; it reads cleaned text from the doc store and picks the best window containing the query terms.
  8. 8Response is assembled, written to the result cache and returned; the query and later clicks are logged to Kafka for training.

Index a crawled page

  1. 1Crawler publishes the fetched page (URL, HTML, headers) to Kafka.
  2. 2Parser extracts text, title, headings, links and language; computes a content hash and a SimHash for near-duplicate detection.
  3. 3If the page is a near-duplicate of a known page, it is folded into the canonical URL instead of creating a new doc.
  4. 4News or high-change sites go to the fresh indexer, which adds them to a small in-memory index served alongside the main shards within minutes.
  5. 5All pages land in the DFS; the daily batch job recomputes PageRank on the link graph, builds new immutable shard files and writes doc store rows.
  6. 6New index version is pushed to replicas gradually (canary shards first), then the root switches traffic to the new version; the old version is kept for fast rollback.

7 Deep dives

Partitioning the index: by document, not by term

There are two ways to split an inverted index. Term partitioning puts all postings for a term on one machine ('cricket' lives on shard 17). A query then touches only the shards for its 2-3 terms, but intersecting long posting lists means shipping huge lists across the network, and popular terms create hot shards. Document partitioning gives each shard a slice of documents (10M pages) and a complete mini-index over them. Every query goes to every shard, but each shard does its own intersection and scoring locally and returns only a tiny top-K list. Load is naturally balanced because documents are assigned by hash. Web search engines use document partitioning. The cost is fan-out: one query becomes 5,000 shard requests, so the slowest shard decides latency. You control that with a tree of aggregators, per-shard deadlines (drop a shard that is late and return a slightly incomplete result), replicas for every shard and hedged requests (send a backup request to another replica if the first has not replied by the p95 time). Mention that the system degrades gracefully: losing one shard loses 0.02% of documents, not the whole query.

Two-stage ranking within the latency budget

You cannot run an expensive model on billions of documents per query, so ranking is a funnel. Stage 0 is retrieval: intersect posting lists (with WAND or block-max WAND to skip documents that cannot make the top-K) and score with BM25 plus a static quality score. Posting lists can be ordered by quality so a shard can stop early once it has enough good candidates. Each shard returns ~50, so the root sees ~250k candidates and keeps ~500. Stage 1 is a learning-to-rank model (gradient-boosted trees like LambdaMART) over a few hundred features: BM25 per field (title, body, anchor text, URL), term proximity, PageRank, spam score, freshness, historical click-through for this query-URL pair. Stage 2, if budget allows, is a small transformer cross-encoder on the top ~50. Budget example: 20 ms query understanding, 60 ms shard retrieval, 30 ms LTR, 40 ms neural re-rank, 20 ms snippets. Training labels come from human raters plus click logs corrected for position bias (people click result #1 because it is #1).

Freshness: batch index plus a real-time tier

Rebuilding a 50-billion-page index takes many hours, so a single batch index is always a day or more stale. That is fine for Wikipedia, not for 'India vs Australia score' or a breaking news story. The standard answer is a tiered index. The main index is rebuilt in batch (daily or every few days), immutable, highly compressed and cheap to serve. A fresh tier is a small index (say 100M recent or fast-changing pages) built from a stream: pages from news sites and high-change URLs flow from Kafka into an indexer that builds small in-memory segments every few seconds, LSM-style, and merges them in the background. At query time the root queries both tiers and merges, with the query understanding step boosting the fresh tier when it detects news intent. Deletions (DMCA takedowns, pages gone 404) are handled with a deleted-doc bitmap checked at serve time, because rewriting immutable segments for one deletion is too expensive.

Dedup and spam

Around a quarter to a third of crawled pages are exact or near duplicates: mirrors, printer versions, URLs with tracking parameters. Exact dupes are caught with a content hash. Near-dupes use SimHash: a 64-bit fingerprint where similar documents differ in few bits; you look for fingerprints within Hamming distance 3 using several permuted tables. Duplicates are clustered under one canonical URL (respecting rel=canonical), which saves index space and stops results pages full of copies. Spam fights the ranking signals. Link farms inflate PageRank, so you compute trust-propagated scores (TrustRank) from a seed set of known-good sites and down-weight links from new or clustered domains. Keyword stuffing is caught by text classifiers. Spam scores are static features in the index, and spammy pages can be dropped at build time.

8 Trade-offs

Document vs term partitioning

Document partitioning wins: balanced load, local scoring, graceful degradation when a shard is down. Term partitioning reduces fan-out but moves huge posting lists over the network and creates hot shards for common terms. Accept the fan-out and fight tail latency with replicas, deadlines and hedging.

Batch-built immutable index vs fully incremental index

Immutable segments compress better, need no locks and are trivially replicated and rolled back. A fully incremental index (like Elasticsearch at this scale) would carry constant merge overhead on every shard. Hybrid: batch main index plus a small streaming fresh tier.

Exact top-K vs approximate

Shards return results within a deadline and may skip low-quality postings (early termination). You lose a tiny amount of recall in exchange for a predictable p99. For web search, a slightly worse result in 150 ms beats a perfect one in 2 s.

Result cache vs always-fresh

Caching the head of the query distribution cuts backend load by 30-50%. The cost is staleness, handled by putting the index version in the cache key and using short TTLs for queries classified as news.

9 Bottlenecks

Tail latency from fan-out

With 5,000 shards, even a 1-in-1,000 slow shard request hits most queries. Fixes: aggregator tree to limit fan-in per node, hedged requests after the p95 time, per-shard deadlines with partial results, keeping index data in RAM/SSD and isolating batch jobs from serving machines.

10x index growth

More documents means more shards and more fan-out. Use a tiered index: a small 'tier 1' of the highest-quality ~10% of pages answers most queries; only go to the full tier when tier 1 returns too few good results. This also cuts serving cost sharply.

10x query traffic

Add replicas of every shard (cost grows linearly), raise the cache hit rate with better query normalisation, and serve from more regions. Shard count does not need to change for traffic growth.

10 Failure handling

An index shard replica dies

Aggregators health-check replicas and route to the others. If all replicas of a shard are slow, the shard is skipped after the deadline and the response is marked partial internally; the user loses 0.02% recall, not the page.

A bad index build ships

New index versions roll out shard by shard to canary replicas with automated quality checks (known-query regression suite, result overlap with the previous version, click metrics). Keep the previous version on disk so rollback is a pointer flip.

A whole data centre goes down

Every region holds a full replica of the index. GeoDNS and anycast move traffic to the next region; capacity planning keeps N+1 regions so a loss raises latency but not error rate.

11 What impresses

  • Explaining why every query hits every shard and then immediately addressing tail latency with hedging and deadlines
  • Giving a per-stage latency budget that adds up to the target
  • Separating query-independent (static) signals computed offline from query-dependent scoring at serve time
  • Mentioning position bias when training rankers from clicks
  • Tiered index (high-quality tier first) as a cost and latency optimisation
  • Treating index builds as deployments: canary, quality checks, versioned rollback

12 Mistakes

  • Saying 'put everything in Elasticsearch' without discussing sharding at 50B documents
  • Designing the crawler for 30 minutes when the interviewer asked about search serving
  • Using a SQL LIKE query or a relational table for full-text search
  • Ignoring ranking entirely and only talking about storage
  • Forgetting that one slow shard sets the latency of the whole query
  • No story for freshness, so news queries return yesterday's pages

13 Follow-ups

Q1How would you support phrase queries like "chief minister of karnataka"?
Q2How do you evaluate whether a new ranking model is better before shipping it?
Q3How do you handle queries in Hindi typed in Latin script (Hinglish)?
Q4How would you add semantic (vector) retrieval alongside the inverted index?
Q5How do you remove a page from results within an hour after a legal takedown?
Q6How would you cut serving cost by half?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free