Want it harder? Hide the model answer and work each phase in a scratchpad against its time budget, then reveal and score yourself. Or simply read on: the checkpoints below work either way.
Answering ~14 Billion Questions a Day — In Milliseconds
Imagine this challenge: Someone types three words into a search box.
Within 200 milliseconds, you must search through 400 billion documents — roughly 3,000 times the number of books ever published — find the most relevant results, rank them by quality and relevance, check for spam, personalize the results, and display them in a beautiful interface.
Now do that ~160,000 times per second on average, every second, 24/7.
This is Google Search — the most sophisticated information retrieval system ever built.
Query volume
- Daily searches: ~14 Billion (older estimates: 8.5 Billion)
- Searches per second: ~160,000 average
- Annual searches: 5+ Trillion (Google, 2025)
- Market share: ~90% globally (StatCounter, 2026)
Index size
- Indexed documents: ~400 Billion (2020 figure, per Google's testimony in US v. Google)
- Known URLs: 130+ Trillion (Google, 2016)
- Index storage: 100+ Petabytes (Google: "well over 100,000,000 gigabytes")
- New URLs discovered: a constant stream (Google publishes no daily figure)
Infrastructure
- Data centers: 30+ locations worldwide (Google's data-centre locations page, 2026)
- Servers (estimated): Millions (Google doesn't disclose; a 2016 third-party estimate put it at ~2.5M)
- Power consumption: ~43.6 TWh/year (all of Google, 2025, mostly data centers)
- Response time: typically a few hundred milliseconds (Google publishes no latency SLO)
Business impact
- Search & other revenue (2025): $224.5 Billion
- Revenue per search: ~$0.045 (rough: $224.5B ÷ ~5T searches)
- Users worldwide: Billions
This is the system we'll design today. Google publishes its scale and some history (the 1998 paper, Jeff Dean's 2009 talk), not its current serving internals: the stage budgets, signal weights, shard counts and tiers below are a likely design for an engine like Google's, not Google's actual system.
Google answers ~14 billion searches a day against ~400 billion documents; this design targets 50 to 500 billion pages, 100,000 QPS and under 500 ms, with 25 trillion postings (~180 TB) that must be sharded. Async pipelines crawl and index, while serving scatters each query to every shard, gathers ~10,000 candidates and ranks them. The deep dives cover the inverted index and document sharding, PageRank, tail latency at 1,000 shards and per-stage budgets, then replicas and caching at 10x, a fresh tier for breaking news, hot queries, spam and crawl traps.
The Interview Begins#
You walk into the interview room. The interviewer smiles and gestures to the whiteboard.
"Thanks for coming in. Today we're going to work through a system design problem together. I want to see how you think about large-scale distributed systems. Feel free to ask questions — this should be collaborative."
They write on the whiteboard:
Build a search engine that can:
- Index billions of web pages
- Return relevant results in <500ms
- Handle 10,000+ queries per second
- Keep the index fresh (new content discoverable within hours)
- Rank results by relevance and quality
Consider
- How do you discover new pages?
- How do you store and query the index efficiently?
- How do you rank billions of documents?
- How do you handle 100K QPS with sub-second latency?
"Take a few minutes to think about this, then walk me through your approach. We have about 45 minutes."
Phase 1: Requirements Clarification (5 minutes)#
Before diving in, you take a breath and start asking questions.
Your Questions#
"Before I start designing, I'd like to clarify a few requirements. First, what's the scale of the web we need to index? Are we talking millions or billions of pages?"
"Let's say we need to index around 50 billion pages initially, with the capability to scale to hundreds of billions."
"And what's our query volume? Peak QPS?"
"Target 10,000 QPS initially, scaling to 100,000 QPS."
"What about freshness requirements? How quickly should new content appear in search results?"
"Critical news should appear within minutes. Regular content within hours. Most content within a few days is acceptable."
"For ranking, should we implement something like PageRank, or simpler relevance scoring?"
"Start with basic relevance like TF-IDF, then layer in link-based authority scoring like PageRank."
"Perfect. Let me summarize the requirements."
Functional Requirements#
1. Web crawling
- Discover and download web pages continuously
- Respect robots.txt and crawl rate limits
- Handle various content types (HTML, PDF, etc.)
- Track page freshness and update frequency
2. Indexing
- Parse and extract text content from pages
- Build searchable index from page content
- Store page metadata (title, URL, timestamp)
- Support incremental index updates
3. Query processing
- Parse and understand user queries
- Retrieve matching documents efficiently
- Rank results by relevance and quality
- Support features: autocomplete, spell check, filters
4. Ranking
- Text relevance scoring (TF-IDF, BM25)
- Link-based authority (PageRank)
- Freshness signals
- Spam detection and filtering
Non-Functional Requirements#
1. Scale
- 50+ billion indexed pages
- ~15+ petabytes of page, index and log data
- 100,000 QPS at peak
2. Latency
- Query response: <500ms p99
- Index freshness: <24 hours for most content
- Breaking news: <15 minutes
3. Availability
- 99.99% uptime
- Graceful degradation under load
- Multi-region deployment
4. Consistency
- Eventually consistent index
- Stale reads acceptable (within hours)
- Strong consistency for user data only
Phase 2: Back of the Envelope Estimation (5 minutes)#
"Let me work through the numbers to understand the scale we're dealing with."
Before the numbers: will the index fit on one machine?
You index 50 billion pages with about 500 distinct terms each, and a posting (document ID, position, metadata) takes about 12 bytes. How big is the inverted index? What about after 5× compression and 3 replicas? What does that imply for serving?
One strong answer
- Postings: 50B × 500 = 25 trillion.
- Raw size: 25T × 12 B = 300 TB.
- Compressed: ÷ 5 = ~60 TB, and ~180 TB with three replicas.
Nowhere near one machine. The index has to be split into thousands of shards served in parallel, with the hot parts in memory, which is why sharding is the first deep dive.
Index Size Estimation#
- Document storage
- Pages to index: 50 billion
- Average page size (compressed): 50 KB
- Raw page storage: 50B × 50KB = 2.5 PB
- Inverted index
- Unique terms: ~10 billion (Zipf: most appear in only a handful of docs)
- Total postings: 50B docs × ~500 distinct terms/doc = 25 trillion (only ~2,500 per term on average)
- Posting entry size: 12 bytes (docID + position + metadata)
- Inverted index size: 25T × 12B = 300 TB
- With compression (5x): ~60 TB
- With replication (3x): ~180 TB total
- Forward index (document → terms)
- Per-document metadata: 1 KB
- Forward index: 50B × 1KB = 50 TB
- With replication: 150 TB
Query Traffic Estimation#
- Query volume
- Peak QPS: 100,000
- Queries per day: ~5 billion
- Average query length: ~3-4 words
- Query processing
- Documents to score per query: ~10,000 candidates
- Scoring time per doc: ~10 microseconds
- Total scoring time: ~100ms
- Results to return: 10-20 per page
- Bandwidth
- Query request size: ~500 bytes
- Response size: ~50 KB
- Peak bandwidth: 100K × 50KB = 5 GB/s
Infrastructure Estimation#
- Storage
- Index storage — ~0.2 PB of replicated postings (Google's real index, ~400B docs incl. stored page data, is 100+ PB)
- Document storage — ~10 PB (compressed)
- Metadata/logs — ~5 PB
- Compute
- Index servers — ~10,000 machines
- Crawlers — ~1,000 machines
- Query servers — ~5,000 machines
- Ranking/ML — ~2,000 machines
- Network
- Internal bandwidth — 100+ Tbps
- External bandwidth — 10+ Tbps
- Data centers
- Minimum — 5-7 regions globally
Requirements and estimation
Q1/3 Why is an eventually consistent index acceptable here, when user data needs strong consistency?
Answer & explanation
Answer: B
The requirements accept stale reads within hours for the index, while freshness for breaking news is handled separately. Forcing every shard into strict agreement would slow queries for little benefit. User data is where correctness actually matters.
Q2/3 Each query scores ~10,000 candidates at ~10 microseconds each. If query expansion doubled the candidates to 20,000 at the same cost, roughly how long would scoring take?
Answer & explanation
Answer: D
20,000 x 10 microseconds is 200ms, double the ~100ms baseline. Scoring cost grows linearly with candidates, which is why retrieval caps how many documents reach the scoring stage.
Q3/3 Which item dominates the storage estimate?
Answer & explanation
Answer: B
Total postings are bounded by documents x distinct terms per document (~25 trillion), so the compressed index is only ~60 TB, far below the ~2.5 PB of stored pages. Multiplying 10 billion terms by a big "average postings per term" is the classic trap: most terms are rare.
Phase 3: High-Level Design (10 minutes)#
"Now let me sketch out the high-level architecture. A search engine has three major components: crawling, indexing, and serving."
Sketch it first: three pipelines, one deadline
Before reading on, name the three big pipelines in a search engine. Which of them has to finish within the user's few-hundred-millisecond budget, and how are the others connected to it?
One strong answer
- Crawling: a URL frontier ordered by importance and freshness, polite fetchers, parsers and link extraction feeding new URLs back.
- Indexing: cleaning and tokenizing pages, building the inverted index, and computing link authority such as PageRank.
- Serving: parsing the query, retrieving candidates from the index, ranking them and returning results.
Only serving sits on the user's latency path. Crawling and indexing run asynchronously and may lag by minutes to hours; they hand finished index segments to the serving tier, so a slow crawl makes results staler but never makes a search slower.
System Architecture#
%% title: Search engine architecture
flowchart TD
subgraph CP["Crawling pipeline"]
UF@{ icon: "sd:queue", label: "URL Frontier" } --> CW@{ icon: "sd:robot", label: "Crawler Workers" }
CW --> CPR["Content Parser"]:::service
CPR --> PS@{ icon: "sd:database", label: "Page Store" }
end
subgraph IP["Indexing pipeline"]
DP["Document Parser"]:::service --> TA["Tokenizer/Analyzer"]:::service
TA --> IB["Index Builder"]:::core
IB --> II@{ icon: "sd:index", label: "Inverted Index" }
DP --> PR["PageRank Compute"]:::core
end
subgraph SL["Serving layer"]
QP["Query Parser"]:::edge ==> RE["Retrieval Engine"]:::hot
RE ==> RK["Ranking Engine"]:::core
RK ==> RS["Results Server"]:::edge
end
CP --> IP
IP --> SL
class UF queue
class CW service
class PS,II storeComponent Breakdown#
1. Crawling Pipeline#
Purpose: Continuously discover and download web pages from the internet.
Key components:
- URL Frontier: Priority queue of URLs to crawl, ordered by importance and freshness
- Crawler Workers: Distributed fetchers that download pages, respecting rate limits
- Content Parser: Extract text, links, and metadata from HTML
- Link Extractor: Discover new URLs to feed back to the frontier
2. Indexing Pipeline#
Purpose: Process crawled pages and build searchable data structures.
Key components:
- Document Parser: Clean HTML, extract text, handle encoding
- Tokenizer/Analyzer: Split text into tokens, apply stemming, remove stopwords
- Index Builder: Construct inverted index data structures
- PageRank Compute: Calculate link-based authority scores
3. Serving Layer#
Purpose: Handle user queries and return ranked results.
Key components:
- Query Parser: Parse query, expand synonyms, handle operators
- Retrieval Engine: Find candidate documents from inverted index
- Ranking Engine: Score and sort candidates by relevance
- Results Server: Format and return final results
High-level design
Q1/2 The crawling pipeline falls a few hours behind. What do users notice?
Answer & explanation
Answer: C
The serving layer reads an index that has already been built, so the query path doesn't depend on crawling. A lagging crawler only means newer pages aren't indexed yet. That's the point of separating crawling, indexing and serving.
Q2/2 A freshly crawled page contains links to 50 pages the system has never seen. What happens to them?
Answer & explanation
Answer: B
Link extraction is how crawling discovers the web. New URLs go into the frontier's priority queue, and crawler workers fetch them in priority order within rate limits, not immediately.
Phase 4: Deep Dives (20 minutes)#
"Great overview. Let's dive deeper into some challenging aspects. How does the inverted index actually work?"
Deep Dive 1: The Inverted Index (Week 1 - Partitioning & Sharding)#
"The inverted index is the heart of any search engine. Let me explain how it works and how we scale it."
What is an Inverted Index?#
Traditional index (forward)
| Document | Words |
|---|---|
| Doc 1 | "the quick brown fox jumps" |
| Doc 2 | "the lazy brown dog sleeps" |
| Doc 3 | "quick fox runs fast" |
Inverted index (reverse)
| Term | Postings List (DocID:Positions) |
|---|---|
| "brown" | [(Doc1, [3]), (Doc2, [3])] |
| "fox" | [(Doc1, [4]), (Doc3, [2])] |
| "quick" | [(Doc1, [2]), (Doc3, [1])] |
| "lazy" | [(Doc2, [2])] |
To find "quick fox"
- Look up "quick" →
{Doc1, Doc3} - Look up "fox" →
{Doc1, Doc3} - Intersect →
{Doc1, Doc3} - Score and rank
Sharding the Index#
"With 400 billion documents and 10 billion unique terms, we need to shard the index across thousands of machines."
Shard by document or by term?
You could give each shard every term for a slice of the documents, or every document for a slice of the terms. Pick one for a web-scale engine and say what each query then has to do.
One strong answer
Document-based sharding (what the lesson's Google design uses, with a tiered index):
- Every query goes to all shards in parallel; each scores its own documents and returns a local top-k, and a merger combines them.
- Shards are self-contained, load spreads evenly, and losing a shard drops a slice of documents rather than whole words.
- The cost is that every query touches every shard, which you pay for with replicas and by searching a small high-quality tier first.
Term-based sharding touches fewer shards per query, but common words create enormous, hot shards, and intersecting postings across machines moves a lot of data over the network.
Option 1: Document-based sharding
Each shard contains ALL terms for a SUBSET of documents
- Shard 1: Docs 0-1M —
"apple" → [...],"banana" → [...] - Shard 2: Docs 1M-2M —
"apple" → [...],"banana" → [...] - Shard N: ...
Query: Search ALL shards in parallel, merge results
- Pro: Each shard is self-contained
- Con: Every query hits every shard
Google uses: Document-based sharding (per Jeff Dean's WSDM 2009 keynote), here with a tiered index
Option 2: Term-based sharding
Each shard contains ALL documents for a SUBSET of terms
- Shard A-F: Terms A-F —
"apple" → all,"banana" → all - Shard G-L: Terms G-L —
"guitar" → all,"house" → all
Query: Only hit shards containing query terms
- Pro: Fewer shards per query
- Con: Cross-shard coordination for scoring
Implementation#
# inverted_index/index_shard.py
"""
Inverted Index Shard Implementation
Demonstrates the core data structures used in search engines.
"""
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from collections import defaultdict
import bisect
import math
@dataclass
class Posting:
"""A single posting entry in the inverted index."""
doc_id: int
term_frequency: int
positions: List[int] = field(default_factory=list)
@dataclass
class PostingsList:
"""List of all postings for a single term."""
term: str
doc_frequency: int
postings: List[Posting] = field(default_factory=list)
def intersect(self, other: 'PostingsList') -> 'PostingsList':
"""
Intersect two posting lists (for AND queries).
Uses merge algorithm - O(n + m).
"""
result = PostingsList(
term=f"{self.term} AND {other.term}",
doc_frequency=0,
postings=[]
)
i, j = 0, 0
while i < len(self.postings) and j < len(other.postings):
if self.postings[i].doc_id == other.postings[j].doc_id:
combined = Posting(
doc_id=self.postings[i].doc_id,
term_frequency=self.postings[i].term_frequency +
other.postings[j].term_frequency,
positions=sorted(self.postings[i].positions +
other.postings[j].positions)
)
result.postings.append(combined)
i += 1
j += 1
elif self.postings[i].doc_id < other.postings[j].doc_id:
i += 1
else:
j += 1
result.doc_frequency = len(result.postings)
return result
class InvertedIndexShard:
"""
A single shard of the inverted index.
In production:
- Each shard handles ~1-10M documents
- Index is memory-mapped for fast access
- Postings are sorted by doc_id for efficient intersection
"""
def __init__(self, shard_id: int, doc_range: tuple):
self.shard_id = shard_id
self.doc_range = doc_range
self.index: Dict[str, PostingsList] = {}
self.doc_count = 0
def add_document(self, doc_id: int, tokens: List[str]) -> None:
"""Index a single document."""
term_positions: Dict[str, List[int]] = defaultdict(list)
for position, token in enumerate(tokens):
term_positions[token].append(position)
for term, positions in term_positions.items():
if term not in self.index:
self.index[term] = PostingsList(term=term, doc_frequency=0)
posting = Posting(
doc_id=doc_id,
term_frequency=len(positions),
positions=positions
)
# key= needs Python 3.10+; insort into a list is O(n) per insert
bisect.insort(
self.index[term].postings,
posting,
key=lambda p: p.doc_id
)
self.index[term].doc_frequency += 1
self.doc_count += 1
def search(self, query_terms: List[str]) -> PostingsList:
"""Search for documents containing all query terms."""
if not query_terms:
return PostingsList(term="", doc_frequency=0)
postings_lists = []
for term in query_terms:
pl = self.index.get(term)
if pl is None:
return PostingsList(term=term, doc_frequency=0)
postings_lists.append(pl)
# Sort by document frequency (shortest first)
postings_lists.sort(key=lambda pl: pl.doc_frequency)
# Intersect all posting lists
result = postings_lists[0]
for pl in postings_lists[1:]:
result = result.intersect(pl)
if result.doc_frequency == 0:
break
return result
def compute_idf(self, doc_frequency: int) -> float:
"""Compute Inverse Document Frequency."""
if doc_frequency == 0:
return 0.0
return math.log(self.doc_count / doc_frequency)Deep Dive 2: PageRank Algorithm (Week 5 - Consistency & Coordination)#
"Tell me about PageRank. How does Google determine page importance?"
"PageRank is the algorithm that made Google famous. It's essentially a voting system where links are votes."
The PageRank Concept#
Imagine a random web surfer:
- Start on a random page
- Follow a random link on that page
- Repeat forever
PageRank = probability that the random surfer lands on a page
Pages with more incoming links → higher probability → higher PageRank
The Mathematics#
PR(A) = (1-d)/N + d × Σ(PR(i)/L(i))
Where:
PR(A)— PageRank of page Ad— Damping factor (typically 0.85)N— Total number of pagesPR(i)— PageRank of pages linking to AL(i)— Number of outgoing links from page i
The damping factor d is the probability that the random surfer keeps following links; with probability 1-d (0.15) they get bored and jump to a random page.
Implementation#
# ranking/pagerank.py
"""
PageRank Computation at Scale
Demonstrates how to compute PageRank for billions of pages.
"""
from dataclasses import dataclass
from typing import Dict, List
@dataclass
class WebPage:
"""Represents a web page in the link graph."""
url: str
outgoing_links: List[str]
incoming_links: List[str] = None
pagerank: float = 1.0
class PageRankComputer:
"""
Computes PageRank scores for a web graph.
At web scale (a likely design, not Google's published pipeline):
- Graph has 100+ billion nodes
- Computation takes days on thousands of machines
- Uses MapReduce-style distributed iteration
"""
def __init__(self, damping_factor: float = 0.85, max_iterations: int = 100,
convergence_threshold: float = 1e-6):
self.damping_factor = damping_factor
self.max_iterations = max_iterations
self.convergence_threshold = convergence_threshold
self.pages: Dict[str, WebPage] = {}
self.num_pages = 0
def add_page(self, url: str, outgoing_links: List[str]) -> None:
"""Add a page to the graph."""
self.pages[url] = WebPage(url=url, outgoing_links=outgoing_links)
for link in outgoing_links:
if link not in self.pages:
self.pages[link] = WebPage(url=link, outgoing_links=[])
self.num_pages = len(self.pages)
def build_reverse_links(self) -> None:
"""Build incoming link lists for each page."""
for page in self.pages.values():
page.incoming_links = []
for url, page in self.pages.items():
for link in page.outgoing_links:
if link in self.pages:
self.pages[link].incoming_links.append(url)
def compute(self) -> Dict[str, float]:
"""
Compute PageRank using power iteration.
Algorithm:
1. Initialize all pages with equal rank (1/N)
2. For each iteration:
- Sum contributions from incoming links
- Apply damping factor
3. Repeat until convergence
"""
if not self.pages:
return {}
self.build_reverse_links()
# Initialize PageRank values
initial_rank = 1.0 / self.num_pages
for page in self.pages.values():
page.pagerank = initial_rank
# Iterative computation
for iteration in range(self.max_iterations):
new_ranks: Dict[str, float] = {}
# Handle dangling pages
dangling_sum = sum(
page.pagerank for page in self.pages.values()
if len(page.outgoing_links) == 0
)
dangling_contribution = dangling_sum / self.num_pages
# Compute new rank for each page
for url, page in self.pages.items():
new_rank = (1 - self.damping_factor) / self.num_pages
new_rank += self.damping_factor * dangling_contribution
for source_url in page.incoming_links:
source_page = self.pages[source_url]
num_outlinks = len(source_page.outgoing_links)
if num_outlinks > 0:
contribution = source_page.pagerank / num_outlinks
new_rank += self.damping_factor * contribution
new_ranks[url] = new_rank
# Check for convergence
max_diff = max(
abs(new_ranks[url] - self.pages[url].pagerank)
for url in self.pages
)
for url, rank in new_ranks.items():
self.pages[url].pagerank = rank
if max_diff < self.convergence_threshold:
print(f"Converged after {iteration + 1} iterations")
break
return {url: page.pagerank for url, page in self.pages.items()}Inverted index and PageRank
Q1/3 The index uses document-based sharding. Which shards does the query "quick fox" go to?
Answer & explanation
Answer: D
Each document shard holds all terms for a subset of documents, so any shard could have matches and every query fans out to all of them. Only term-based sharding sends a query to just the shards that own its terms, and that design pays for it with cross-shard scoring coordination.
Q2/3 Before intersecting,
searchsorts posting lists by document frequency, shortest first. Why?Answer & explanation
Answer: B
Starting with the rarest term keeps every intersection small, and the loop exits as soon as the result is empty. Postings still have to be sorted by doc_id, because that's what makes the O(n + m) merge work.
Q3/3 Pages A and B each have one incoming link. Both source pages have equal PageRank, but A's source has 100 outgoing links and B's source has 2. Who gets more PageRank from that link?
Answer & explanation
Answer: C
A page's PageRank is divided among its outgoing links. B's source gives half its rank to each of 2 links, while A's source spreads its rank over 100. So links aren't equal votes: a link from a selective page counts for more.
Deep Dive 3: Query Processing Pipeline (Week 2 - Timeouts & Latency)#
"Walk me through what happens when a user types a query."
"Query processing is where all the latency pressure is. We have 500ms to do everything."
The slowest shard sets the pace
A query fans out to 1,000 shards and waits for all of them. Each shard answers quickly 99% of the time but is occasionally slow. How often does a query hit at least one slow shard, and what would you do about it?
One strong answer
- The maths: the chance that all 1,000 are fast is 0.99^1000 ≈ 0.004%, so almost every query waits on at least one slow shard. At this fan-out, the tail of one shard becomes the median of the whole query.
- Deadlines and partial results: give each shard a budget and return what has arrived; a slightly incomplete result list beats a slow one.
- Hedged requests: if a shard hasn't answered within a few milliseconds, ask one of its replicas too and take whichever answers first.
- Do less work per query: cache results for popular queries, and search the small top tier first, going deeper only when needed.
Query Processing Flow#
%% title: Query processing timeline (target < 500ms)
flowchart TD
U(["User types: 'best pizza near me'"]):::client
subgraph S1["Stage 1 · Query understanding (20ms budget)"]
direction TB
SP["Spell Check<br/>'piza' → 'pizza'"]:::service
TK["Tokenize<br/>Split words"]:::service
SY["Synonyms<br/>best → top"]:::service
IN["Intent<br/>Local search"]:::service
end
RT["STAGE 2 · RETRIEVAL (100ms budget)<br/>Query sent to all index shards in parallel<br/>Intersect posting lists, best candidates per shard merged<br/>Total candidates kept: ~10,000 documents"]:::hot
SC["STAGE 3 · SCORING (200ms budget)<br/>Text Relevance (BM25): Weight 0.3<br/>PageRank: Weight 0.2 · Freshness: Weight 0.1<br/>Location Match: Weight 0.2 · Click History: Weight 0.2<br/>Sort by final score, take top 100"]:::core
SN["STAGE 4 · SNIPPET GENERATION (50ms budget)<br/>For top 10 results: fetch document content (or cached snippet)<br/>Find best passage containing query terms<br/>Highlight matching terms"]:::service
RS(["STAGE 5 · RESPONSE (30ms budget)<br/>Total time: ~400ms (target < 500ms)"]):::ok
U --> S1
S1 ==> RT
RT ==> SC
SC ==> SN
SN ==> RSA query fans out to every document shard in parallel and each returns its local top-k. Waiting for all lets one slow shard set the latency: with 1,000 shards only about 0.004% of queries avoid a slow one. With a deadline and a hedged request to the slow shard's replica, the first answer wins and the root merges about 10,000 candidates inside the 100 ms retrieval budget.
BM25 Scoring Implementation#
# query/scorer.py
"""
BM25 Ranking Algorithm
An improvement over TF-IDF that handles term frequency saturation
and document length normalization.
"""
import math
from typing import Dict, List, Optional
class BM25Scorer:
"""
BM25 scoring for search relevance.
BM25 is the industry standard for text relevance scoring.
It improves on TF-IDF by:
- Saturating term frequency (diminishing returns)
- Normalizing for document length
"""
K1 = 1.2 # Term frequency saturation parameter
B = 0.75 # Length normalization parameter
def __init__(self, avg_doc_length: float, total_docs: int):
self.avg_doc_length = avg_doc_length
self.total_docs = total_docs
def score(self, doc: Dict, query_terms: List[str]) -> float:
"""
Compute BM25 score for a document.
Args:
doc: Document with term_freqs, doc_length, doc_freqs
query_terms: List of query terms
Returns:
BM25 score (higher = more relevant)
"""
score = 0.0
doc_length = doc.get("doc_length", self.avg_doc_length)
term_freqs = doc.get("term_freqs", {})
for term in query_terms:
tf = term_freqs.get(term, 0)
if tf == 0:
continue
# IDF component
df = doc.get("doc_freqs", {}).get(term, 1)
idf = math.log((self.total_docs - df + 0.5) / (df + 0.5) + 1)
# TF component with saturation
numerator = tf * (self.K1 + 1)
denominator = tf + self.K1 * (
1 - self.B + self.B * (doc_length / self.avg_doc_length)
)
score += idf * (numerator / denominator)
return score
def compute_freshness(self, last_updated) -> float:
"""Compute freshness score with exponential decay."""
from datetime import datetime
if last_updated is None:
return 0.5
age_days = (datetime.utcnow() - last_updated).days
half_life = 30 # 30-day half-life
return math.exp(-0.693 * age_days / half_life)Deep Dive 4: Web Crawling at Scale (Week 3 - Async Processing)#
"How do you crawl billions of pages efficiently?"
"Crawling is fundamentally an async, distributed problem."
Crawling Architecture#
%% title: Web crawling pipeline
flowchart TD
UF@{ icon: "sd:queue", label: "URL FRONTIER<br/>Priority Queue of URLs to crawl, ordered by:<br/>Importance (PageRank of linking pages)<br/>Freshness (time since last crawl)<br/>Domain priority (news sites crawled more often)" }
CW@{ icon: "sd:robot", label: "CRAWLER WORKERS<br/>1000s of distributed workers fetching pages in parallel" }
CPR["CONTENT PROCESSOR<br/>Parse HTML → Extract Links & Text →<br/>Detect Language → Store Content"]:::service
UF ==> CW
CW ==> CPR
CPR -.->|"New URLs fed back"| UF
class UF queue
class CW corePOLITENESS CONSTRAINTS (crawler workers):
- Max 1 request per second per domain
- Respect robots.txt
- Respect Crawl-Delay directive
Crawler Implementation#
# crawler/web_crawler.py
"""
Distributed Web Crawler
Demonstrates key crawling concepts:
- Politeness (rate limiting per domain)
- Priority-based URL frontier
- Parallel fetching with async IO
"""
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Set
from datetime import datetime
from urllib.parse import urlparse, urljoin
import asyncio
import heapq
@dataclass
class CrawlURL:
"""A URL in the crawl frontier."""
url: str
priority: float
last_crawl: Optional[datetime] = None
depth: int = 0
def __lt__(self, other):
return self.priority > other.priority
class URLFrontier:
"""
Priority queue of URLs to crawl.
Key features:
- Priority-based ordering
- Deduplication
- Per-domain queuing for politeness
"""
def __init__(self, max_size: int = 10_000_000):
self.max_size = max_size
self.queue: List[CrawlURL] = []
self.seen_urls: Set[str] = set()
self.domain_last_access: Dict[str, datetime] = {}
def add(self, url: str, priority: float = 0.5, depth: int = 0) -> bool:
"""Add URL to frontier if not seen."""
url = url.lower().rstrip("/").split("#")[0]
if url in self.seen_urls:
return False
if len(self.seen_urls) >= self.max_size:
return False
crawl_url = CrawlURL(url=url, priority=priority, depth=depth)
heapq.heappush(self.queue, crawl_url)
self.seen_urls.add(url)
return True
def get_next(self) -> Optional[CrawlURL]:
"""Get next URL respecting politeness constraints."""
if not self.queue:
return None
now = datetime.utcnow()
deferred: List[CrawlURL] = [] # blocked by politeness; re-queue after the scan
result: Optional[CrawlURL] = None
while self.queue:
crawl_url = heapq.heappop(self.queue)
domain = urlparse(crawl_url.url).netloc
last_access = self.domain_last_access.get(domain)
if last_access and (now - last_access).total_seconds() < 1.0:
deferred.append(crawl_url)
continue
self.domain_last_access[domain] = now
result = crawl_url
break
for item in deferred:
heapq.heappush(self.queue, item)
return result
class WebCrawler:
"""
Distributed web crawler with async IO.
"""
def __init__(self, num_workers: int = 100, max_pages: int = 1_000_000):
self.num_workers = num_workers
self.max_pages = max_pages
self.frontier = URLFrontier()
self.pages_crawled = 0
def add_seeds(self, seed_urls: List[str]) -> None:
"""Add seed URLs to start crawling."""
for url in seed_urls:
self.frontier.add(url, priority=1.0, depth=0)
async def crawl(self) -> None:
"""Start crawling with multiple workers."""
workers = [
asyncio.create_task(self._worker(i))
for i in range(self.num_workers)
]
await asyncio.gather(*workers)
async def _worker(self, worker_id: int) -> None:
"""Single crawler worker."""
import aiohttp
async with aiohttp.ClientSession(
headers={"User-Agent": "GoogleBot/2.1"}
) as session:
while self.pages_crawled < self.max_pages:
crawl_url = self.frontier.get_next()
if crawl_url is None:
await asyncio.sleep(0.1)
continue
try:
async with session.get(
crawl_url.url,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
if response.status == 200:
content = await response.text()
links = self._extract_links(crawl_url.url, content)
for link in links:
self.frontier.add(
link, priority=0.5,
depth=crawl_url.depth + 1
)
self.pages_crawled += 1
except Exception:
pass
def _extract_links(self, base_url: str, html: str) -> List[str]:
"""Extract links from HTML content."""
import re
links = []
href_pattern = re.compile(r'href=["\']([^"\']+)["\']')
for match in href_pattern.finditer(html):
href = match.group(1)
if href.startswith(("javascript:", "mailto:", "#")):
continue
absolute_url = urljoin(base_url, href)
if absolute_url.startswith(("http://", "https://")):
links.append(absolute_url)
return linksQuery processing and crawling
Q1/3 Two documents of similar length mention "pizza" 50 times and 5 times. How does BM25 treat that gap compared with raw term frequency?
Answer & explanation
Answer: C
The
k1term gives diminishing returns, so repeating a word keeps adding less and less. That's one of BM25's two improvements over TF-IDF; the other is length normalization. It doesn't flag spam by itself.Q2/3 The top 1,000 URLs in the frontier all belong to one news site. With a limit of 1 request per second per domain, what does the frontier do?
Answer & explanation
Answer: A
get_nextskips a URL whose domain was hit less than a second ago and moves on to other work. High priority decides the order, but it never overrides politeness: hammering one site would overload it and get the crawler blocked.Q3/3 Why split the 500ms target into per-stage budgets (understanding 20ms, retrieval 100ms, scoring 200ms, and so on)?
Answer & explanation
Answer: D
With a fixed budget per stage, each stage can cut off and move on with what it has, which keeps the end-to-end p99 under control. Without budgets, a single slow step pushes the whole response past its target.
Phase 5: Scaling and Edge Cases (5 minutes)#
"How would you scale this to handle 10x traffic?"
Scaling Strategy#
Query path
Current: 10K QPS → Target: 100K QPS
Strategy:
- Add more query servers (10x)
- Increase index shard replicas (3x → 10x)
- Add query result cache (hit rate ~30%)
- Deploy in more regions (latency optimization)
Index path
Current: 50B documents → Target: 500B documents
Strategy:
- Add more index shards (10x)
- Tiered storage (hot/warm/cold)
- Better compression (reduce storage cost)
Edge Cases#
Edge Case 1: Query Spikes#
- Scenario: Breaking news causes 10x traffic spike
- Problem: Index shards for hot terms get overwhelmed
- Solution:
- Query result caching with short TTL
- Replicate hot index shards dynamically
- Graceful degradation (return slightly stale results)
Edge Case 2: Index Freshness#
- Scenario: Celebrity dies, but search shows them as "alive"
- Problem: Cached PageRank and index data is stale
- Solution:
- Breaking news detection pipeline
- Fast-path indexing for high-priority content
- Real-time signals from trusted sources
Edge Case 3: Spam Attacks#
- Scenario: SEO farms create millions of spam pages
- Problem: Index quality degrades
- Solution:
- Link graph analysis (detect link farms)
- Content fingerprinting (detect duplicates)
- ML spam classifiers
Edge Case 4: Crawl Traps#
- Scenario: Website generates infinite URLs
- Problem: Crawler gets stuck, wastes resources
- Solution:
- Detect URL patterns generating infinite content
- Limit crawl depth per domain
- Track unique content ratio
Scaling and edge cases
Q1/3 To go from 10K to 100K QPS without growing the corpus, why add index shard replicas rather than more shards?
Answer & explanation
Answer: C
Query throughput is limited by how many copies of each shard can answer. More shards is the lever for growing from 50B to 500B documents, not for serving more queries.
Q2/3 A site generates an endless stream of unique URLs, like a calendar with a "next month" link that never ends. What protects the crawler?
Answer & explanation
Answer: B
A crawl trap produces new URLs with little new content. Depth limits and a unique-content ratio catch it. Crawling faster would only waste more resources on the trap.
Q3/3 Breaking news causes a 10x spike in identical queries. Why use a result cache with a short TTL?
Answer & explanation
Answer: A
Thousands of users sending the same query can share one computed result, which takes load off the hot shards. A short TTL accepts slightly stale results in exchange for that protection, without freezing results for a story that's still developing.
Phase 6: Monitoring and Operations#
Monitoring Dashboard#
Query performance
| Metric | Value | Gauge |
|---|---|---|
| QPS | 87,432 /sec | 87% |
| Latency p50 | 145 ms | 29% |
| Latency p99 | 412 ms | 82% |
| Error rate | 0.02% | OK |
Index health
| Metric | Value | Gauge |
|---|---|---|
| Indexed docs | 48.2B | 96% |
| Index freshness | 2.3 hours avg | OK |
| Shard replicas healthy | 99.97% | OK |
Quality signals
| Metric | Value | Gauge |
|---|---|---|
| Click-through rate | 34.2% | ↑2% |
| Spam detection rate | 99.3% | OK |
Alerting Strategy#
CRITICAL (Page immediately)
- Query latency p99 > 1s for > 2 min
- Error rate > 1%
- Index shard unavailable
WARNING (Slack notification)
- Query latency p99 > 500ms for > 5 min
- Index freshness > 12 hours
- Click-through rate drops > 10%
Watching search quality
Q1/3 Click-through rate drops by more than 10% while latency and error rate look normal. What is the most likely cause?
Answer & explanation
Answer: B
Users clicking less on fast, error-free pages points at relevance. Quality metrics catch regressions that infrastructure metrics never see, which is why CTR has its own alert.
Q2/3 Index freshness climbs from 2 hours to 14 hours while query latency is fine. Where is the problem?
Answer & explanation
Answer: B
Serving only reads finished index segments, so it stays fast while the pipeline behind it falls behind. Freshness is the metric that exposes a stalled crawl or indexer.
Q3/3 With document-based sharding, why is "index shard unavailable" a critical alert even if every query still returns results?
Answer & explanation
Answer: B
A missing document shard doesn't break queries, it quietly removes a share of the web from all of them. Silent incompleteness is exactly the kind of failure that needs a loud alert.
Final Challenge: Breaking News in Fifteen Minutes#
The interviewer checks the time. "Let's end on freshness."
"A major earthquake hits a big city. Within minutes, millions of people search for it, and the pages that matter were published five minutes ago. Your requirement says breaking news within 15 minutes. How does that work in your design?"
- Relevant pages are minutes old, so they're in no index shard yet
- One topic suddenly takes a large share of all queries
- Rumour and spam pages appear just as fast as real reporting
- Everything else must keep working as normal
Fresh results without rebuilding the world
Rebuilding or merging hundreds of terabytes of index takes hours. How do minutes-old pages reach results, and how do you keep one exploding query from overloading every shard?
One strong answer
- A fast path into the index: prioritised crawling of feeds, sitemaps and trusted news sources, with new pages indexed incrementally into a small, in-memory fresh tier that takes writes in seconds and is merged into the main index later.
- Query both tiers: the query hits the main index and the fresh tier and merges the results.
- Rank for the moment: detect the query spike, treat it as news-seeking, and boost freshness and source authority for it.
- Protect serving: cache results for the hot query and its variants with a short TTL so repeats don't fan out to every shard, and add replicas for the overloaded shards.
- Guard quality: lean on source trust and spam signals, since breaking news is exactly when bad pages try to rank.
Final challenge · Quest check
Q1/4 Why add a small fresh tier instead of rebuilding the main index more often?
Answer & explanation
Answer: B
Big immutable segments are efficient to serve but slow to rebuild. A small mutable tier absorbs new pages immediately and is folded into the main index later.
Q2/4 One query suddenly makes up a large share of all traffic. What is the quickest effective protection?
Answer & explanation
Answer: A
Identical queries within a few seconds can share one answer, so most of the spike never fans out to the shards. A short TTL keeps the cached results fresh enough for news.
Q3/4 A query fans out to 1,000 shards, each fast 99% of the time. Roughly how likely is it that all of them are fast?
Answer & explanation
Answer: D
0.99^1000 ≈ 0.00004. That's why fan-out systems rely on deadlines, partial results and hedged requests rather than waiting for the slowest shard.
Q4/4 In PageRank with damping factor d = 0.85, what does 1 − d = 0.15 represent?
Answer & explanation
Answer: B
The random surfer follows a link with probability d and jumps anywhere with probability 1 − d. The jump is what guarantees every page a small base score and makes the computation converge.
Interview Conclusion#
"Excellent work. You've demonstrated strong understanding of distributed systems, information retrieval, and the specific challenges of building a search engine. Any questions?"
"Thank you! I'd love to hear more about how your team handles the cold start problem for new content — how quickly can fresh content appear in search results?"
Summary: Concepts Applied#
- Week 1: Data at scale
- Partitioning — Index sharded by document ID
- Replication — Each shard replicated 3-10x
- Hot Keys — Popular queries cached, hot shards replicated
- Week 2: Failure-first design
- Timeout budgets — Query stages have strict time limits
- Graceful degradation — Return partial results on timeout
- Circuit breakers — Stop sending to unhealthy shards
- Week 3: Messaging & async
- Async processing — Crawler uses async IO
- Rate limiting — Per-domain crawl limits
- Backpressure — URL frontier limits prevent exhaustion
- Week 5: Consistency & coordination
- Eventual consistency — Index updates over minutes/hours
- Distributed computation — PageRank via MapReduce
- Week 10: Production readiness
- SLOs — Query latency p99 < 500ms, 99.99% availability
- Observability — QPS, latency, error rates, quality signals
Scale
- ~14 billion queries per day (5T+/year) answered in milliseconds
- ~400 billion documents indexed and searchable (2020 figure)
- 130+ trillion URLs known to exist on the web (Google, 2016)
Latency
- Sub-500ms response time for complex queries (this design's target; Google publishes no latency SLO)
- Results from thousands of index shards merged in real time (this design)
Quality
- PageRank revolutionized web search quality
- 200+ ranking signals for relevance (Google's long-cited figure)
- 99%+ of search visits spam-free (Google's webspam reports, 2020-2021)
Google's mission: "to organize the world's information and make it universally accessible and useful."
Sources#
Official Documentation:
- Read
Google: How Search Works - https://www.google.com/search/howsearchworks/
- Docs
Google Search Central - https://developers.google.com/search/docs
Statistics:
- Read
DemandSage: Google Search Statistics 2025 - https://www.demandsage.com/google-search-statistics/
- Read
Search Engine Land: Google 5 Trillion Searches/Year - https://searchengineland.com/google-5-trillion-searches-per-year-452928
- Docs
Zyppy: Google Index Size (400B docs) - https://zyppy.com/seo/google-index-size/
- Read
Alphabet 2025 Form 10-K (Search & other revenue) - https://www.sec.gov/Archives/edgar/data/1652044/000165204426000018/goog-20251231.htm
- Blog
Google 2026 Environmental Report - https://blog.google/company-news/outreach-and-initiatives/sustainability/2026-environmental-report/
- Read
StatCounter: Search Engine Market Share - https://gs.statcounter.com/search-engine-market-share
- Read
Google: Rigorous testing (2023 experiment counts) - https://www.google.com/search/howsearchworks/how-search-works/rigorous-testing/
- Blog
Google: Overview of our rater guidelines - https://blog.google/products/search/overview-our-rater-guidelines-search/
- Paper
Google: Search Quality Rater Guidelines, An Overview (Nov 2023, ~16,000 raters) - https://services.google.com/fh/files/misc/hsw-sqrg.pdf
- Docs
Google: Data center locations - https://datacenters.google/locations/
Architecture:
- Paper
Original Google Paper - http://infolab.stanford.edu/~backrub/google.html
- Wiki
PageRank Algorithm - https://en.wikipedia.org/wiki/PageRank
- Wiki
Google Data Centers - https://en.wikipedia.org/wiki/Google_data_centers
Further Reading#
Books:
- Book
"Introduction to Information Retrieval" by Manning, Raghavan, Schütze
- Book
"The Google Story" by David Vise
Papers:
- Read
"The PageRank Citation Ranking" - Page, Brin, Motwani & Winograd, 1998
- Read
"MapReduce: Simplified Data Processing" - Dean & Ghemawat, 2004
- Read
"The Google File System" - Ghemawat et al., 2003
Self-Assessment Checklist#
After studying this case study, you should be able to:
- Explain how an inverted index works
- Describe the PageRank algorithm
- Design a distributed crawling system
- Explain how to shard an index across thousands of machines
- Describe the query processing pipeline with timeout budgets
- Calculate back-of-envelope estimates for a search engine
- Explain BM25 scoring
- Describe strategies for index freshness
- Design monitoring and alerting for search quality
Appendix: Advanced Topics#
D. HITS Algorithm (Hubs and Authorities)#
- Authority: Page with good content (many pages link TO it)
- Hub: Page that links to good content (links TO many authorities)
Mutually reinforcing:
- Good hubs link to good authorities
- Good authorities are linked from good hubs
Algorithm:
- Initialize all pages with authority=1, hub=1
- Authority update: auth(p) = Σ hub(q) for all q linking to p
- Hub update: hub(p) = Σ auth(q) for all q that p links to
- Normalize scores
- Repeat until convergence
Use case:
- Query-specific ranking (PageRank is query-independent)
- Find topic experts and resource pages
E. Real-Time Search Signals#
Query Click Data#
Google has confirmed that it uses click and interaction data in ranking (its Navboost system, in testimony in US v. Google, 2023). It hasn't published the signals; a likely design would extract:
Click-through rate (CTR)
- Higher CTR = More relevant for that query
- Adjusted for position (top results naturally get more clicks)
Dwell time
- Time spent on page after clicking
- Long dwell = User found what they wanted
- Short dwell + back button = Poor result ("pogo-sticking")
Query reformulation
- User searches again with different terms?
- Original results were poor
Last click
- The final result user clicked (didn't come back)
- Strong signal of satisfaction
Privacy considerations (in this design):
- Data is aggregated (no individual tracking)
- Minimum threshold of queries before using signal
- Used for ranking, not targeting
Breaking News Detection#
Goal: Index critical news within minutes
%% title: Breaking news architecture
flowchart TD
subgraph SS["Signal sources"]
direction TB
TT@{ icon: "sd:chart", label: "Twitter Trends" }
GT@{ icon: "sd:chart", label: "Google Trends" }
NF@{ icon: "sd:feed", label: "News Feeds" }
GA@{ icon: "sd:bell", label: "Google Alerts" }
end
BD["BREAKING NEWS DETECTOR<br/>Spike detection in query volume<br/>New entity mentions (people, places, events)<br/>Sentiment shift detection<br/>Cross-source corroboration"]:::core
PQ@{ icon: "sd:queue", label: "PRIORITY CRAWL QUEUE<br/>Jump queue for news sources<br/>Crawl trusted sources first (AP, Reuters, BBC)<br/>Multiple crawls as story develops" }
RI@{ icon: "sd:index", label: "REAL-TIME INDEX (TIER 0)<br/>Bypasses normal index build pipeline<br/>Direct injection into serving index<br/>Searchable within 2-5 minutes" }
TT & GT & NF & GA --> BD
BD ==> PQ
PQ ==> RI
class TT,GT,NF,GA external
class PQ queue
class RI storeF. Geographic and Personalized Search#
Geolocation in Search#
User query: "pizza"
Without location: Generic results about pizza
With location (user in NYC):
- Detect user location (IP, GPS, profile)
- Expand query:
"pizza" → "pizza near [lat, lng]" - Boost local business results
- Show map pack with nearby options
Location signals:
Explicit
- Query contains location: "pizza in brooklyn"
- User profile location setting
- GPS from mobile device
Implicit
- IP geolocation (~city level accuracy)
- WiFi network location
- Search history patterns
- Time zone from browser
Search Personalization#
Google says your location, past Search history and Search settings help tailor results (How Search Works). The signals below are a likely design for an engine's personalization, not Google's published list:
1. Search history
- Previous queries indicate interests
- "java" for programmer → programming results
- "java" for traveler → Indonesia results
2. Location history
- Frequent locations affect local results
- Home vs work vs travel
3. Device type
- Mobile: Emphasize mobile-friendly pages
- Desktop: May show more detailed results
4. Language preferences
- Browser language
- Past language selections
5. Signed-in account data
- With personal results turned on, Google can show a user's own items, such as Gmail flight confirmations or Calendar events, as separate results
- Interests from other services (an assumption, not a published Google signal)
Privacy controls:
- Users can disable personalization
- Incognito mode = no account-based personalization
- Data can be deleted from account
G. Search Quality Evaluation#
Human Raters and E-E-A-T#
Google works with about 16,000 external "Search Quality Raters" (contractors, per Google's 2023 overview) who:
- Evaluate search results for quality
- Don't directly affect rankings
- Train ML models and validate algorithm changes
E-E-A-T Framework (Experience, Expertise, Authoritativeness, Trustworthiness)
| Pillar | Question | Example |
|---|---|---|
| Experience | Does the content creator have first-hand experience? | Product review from someone who actually used it |
| Expertise | Does the creator have relevant knowledge/skill? | Medical advice from a doctor |
| Authoritativeness | Is the creator/site recognized as a go-to source? | Government site for tax information |
| Trustworthiness | Is the content accurate, honest, safe? | Secure site, accurate facts, transparent authorship |
YMYL (Your Money or Your Life) pages
- Health, finance, safety, legal topics
- Held to higher E-E-A-T standards
- Misinformation can cause real harm
A/B Testing Search Changes#
Google evaluates proposed changes with raters and live-traffic experiments before launch (How Search Works). A likely experiment setup:
Experiment setup
- Control group: Current algorithm
- Treatment group: New algorithm
- Random 1-5% of traffic to treatment
Metrics measured
- Click-through rate (CTR)
- Time to first click
- Pogo-stick rate (quick backs)
- Query abandonment rate
- Long clicks (dwell time > 30s)
- Query reformulation rate
Launch criteria
- Statistically significant improvement
- No regression on key metrics
- Human rater evaluation positive
- No increase in policy violations
Google runs 10,000+ live-traffic experiments per year (16,871 in 2023) — and launched 4,781 changes in 2023.
H. The Future: AI in Search#
Neural Ranking Models#
- Traditional (2000s): TF-IDF + PageRank + Hand-crafted features → Linear combination of ~50 signals
- Learning to Rank (2010s): Gradient boosted trees (LambdaMART) → Nonlinear combination of ~200 signals
- Neural Ranking (2020s): BERT, T5, and transformer models → Semantic understanding of queries and documents
BERT in search
Query: "can you get medicine for someone pharmacy"
Before BERT:
- Matches keywords: medicine, pharmacy
- Misses intent: picking up prescription for another person
With BERT:
- Understands "for someone" = on behalf of another person
- Returns results about pharmacy pickup policies
Key insight: BERT understands prepositions and context
Generative AI Search (AI Overviews)#
Google now generates AI summaries for many queries.
Query: "how to remove coffee stains from carpet"
Traditional results:
- [Link to WikiHow article]
- [Link to cleaning blog]
- [Link to product review]
AI Overview:
"To remove coffee stains from carpet:
- Blot (don't rub) excess liquid immediately
- Mix 1 tablespoon dish soap + 1 tablespoon vinegar + 2 cups water
- Apply solution to stain, blot with clean cloth
- Rinse with cold water and blot dry
[Sources: WikiHow, Good Housekeeping, The Spruce]"
Challenges:
- Attribution and copyright
- Accuracy verification
- Impact on publisher traffic
- Hallucination prevention
- Far more compute per query than classic retrieval
This comprehensive appendix covers advanced topics in search engine design that demonstrate the depth and complexity of building a system like Google Search. These concepts build on the foundational Week 1-10 curriculum and show how theory translates to production systems at massive scale.
Document Statistics:
- Total concepts covered: 25+
- Code implementations: 4
- Architecture diagrams: 4
- Real-world scale numbers: 50+
this lesson in shorts · 3
play them all →One idea from this lesson in about a minute each, made for your phone.
- 0/7stages cleared
Follow-up drills
0 of 10 practisedYou presented Google Search. Now the interviewer pushes back. Answer each follow-up out loud or in the box, then reveal a strong answer.
“How big is the inverted index for 50 billion pages, and what part of storage actually dominates?”
Pushes on · estimating postings without the classic trap
Go deeper · reading trail
all reading →Real write-ups from the teams that built these systems. Each opens on the publisher's site in a new tab.
The Anatomy of a Large-Scale Hypertextual Web Search Engine
Sergey Brin, Lawrence Page · 1998- What
- The paper describing the original Google prototype.
- Key idea
- Crawling, a forward and inverted index split into barrels, and PageRank computed from the link graph to rank results.
- Why here
- The crawl, index and rank pipeline of this quest at its origin, including the PageRank deep dive.
Large-scale Incremental Processing Using Distributed Transactions and Notifications
Daniel Peng, Frank Dabek · 2010- What
- The paper on Percolator, the system that let Google update its index continuously.
- Key idea
- Cross-row transactions and notifications on top of Bigtable let each crawled document update the index incrementally instead of rebuilding it in batch.
- Why here
- The indexing pipeline of this quest when freshness matters: incremental updates instead of periodic full rebuilds.
Automatically generating and ranking results
- What
- Google's public explanation of how Search ranks results.
- Key idea
- Ranking weighs many signals (meaning of the query, relevance, quality, usability, context) and the weight of each depends on the query, such as freshness for news.
- Why here
- The serving layer of this quest: BM25-style relevance is one input among many, and query intent decides how they are mixed.
New lessons by email
New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.
Discussion · …
Public: everyone reading this lesson sees comments. Be kind and keep it on the material.


