01

Requirements & scope

Problem Statement & Requirements

Functional Requirements

  • Prefix matching -- as the user types each character, return the top suggestions matching the prefix
  • Top-K suggestions -- return the K most popular/relevant suggestions (typically K=5-10)
  • Real-time updates -- incorporate trending queries into suggestions quickly (minutes, not days)
  • Personalization -- blend user's own search history into suggestions
  • Multi-language support -- handle queries in multiple languages and scripts
  • Spell tolerance -- handle minor typos in prefix (fuzzy matching)
  • Phrase completion -- suggest completions for multi-word queries ("how to b" → "how to bake bread")
  • Trending queries -- boost currently trending topics (sports events, breaking news)
  • Offensive content filtering -- suppress suggestions for hateful, explicit, or harmful content
  • Entity-aware suggestions -- recognize and boost entities (people, places, brands)

Non-Functional Requirements

  • Ultra-low latency -- suggestions must appear in < 100 ms (ideally < 50 ms)
  • High availability -- 99.99% uptime; degraded suggestions are better than none
  • Massive scale -- serve 100K+ suggestion requests per second
  • Consistency -- eventual consistency is acceptable (different users may see slightly different suggestions)
  • Scalability -- handle billions of unique queries in the corpus
  • Bandwidth efficiency -- minimize data transfer (users type fast, requests are frequent)

Scope Boundaries

In ScopeOut of Scope
Prefix-based query suggestionsFull search results (covered in Search Engine design)
Popularity-based rankingComplex ML-based ranking (simplified)
Real-time trending updatesAd suggestions in autocomplete
Personalization (basic)Voice-based autocomplete
Offensive content filteringDetailed NLP / LLM suggestions
Multi-language supportCross-language translation suggestions
02

Scale estimations

Scale Estimations

Query & Suggestion Corpus

MetricValue
Unique search queries (all time)10B
Unique queries with meaningful frequency500M
Average query length20 characters (4 words)
New unique queries per day50M
Trending queries (burst)100K at any given time

Traffic

MetricValue
Daily Active Users (DAU)500M
Searches per user per day5
Total searches per day2.5B
Keystrokes per search (avg)10 (users type partial, then click suggestion)
Autocomplete requests per day2.5B × 10 = 25B
Requests per second (avg)~290K RPS
Requests per second (peak)~600K RPS

Storage

MetricValue
Suggestion corpus (500M queries × 20 bytes avg)~10 GB
Trie structure overhead~30 GB (pointers, metadata)
Per-user personalization (100M active × 1 KB)~100 GB
Frequency/popularity scores~4 GB
Total in-memory dataset~50 GB per replica

Bandwidth

MetricValue
Average response size (10 suggestions × 30 chars)~500 bytes
Outbound bandwidth290K × 500 bytes = ~145 MB/s
Inbound bandwidth (prefix queries)290K × 50 bytes = ~15 MB/s
03

Layered architecture

Phase 1 of 4 · Client & edge

Start with the entry points

First, define who uses the system and where requests enter. This is the boundary of the design, not yet a complete request path.

Search box
Debounce keystrokes and discard stale responses using the current query sequence.
Edge cache
Serve safe, non-personalized common prefixes close to users.
Architecture after phase 1. Includes Search box, Edge cache. Search box connects to Edge cache.
Phase 1 — Start with Search box, Edge cache.SVGSVGEditable diagramEditable diagram

This first layer defines the system boundary. The next chapters add application logic and data dependencies. After examining the request paths and tradeoffs, we will add operational safeguards and bring the complete architecture together.

04

API & contracts

Phase 2 of 4 · Application & compute

Add the application logic

Keep the entry points and add the services that do the work. Give each service a clear responsibility before defining its contracts. Storage and background dependencies come next.

Suggestion service
Query an in-memory trie or compressed alternative for a small top-k candidate set.
Rank + filter
Blend global, trending, and personal signals, then remove prohibited suggestions.
Architecture after phase 2. Includes Search box, Edge cache, Suggestion service, Rank + filter. Search box connects to Edge cache. Edge cache connects to Suggestion service. Suggestion service connects to Rank + filter.
Phase 2 — Added Suggestion service, Rank + filter. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Send the prefix with language and any allowed personalization context, and return a short ranked list. The client should associate each reply with the prefix that requested it and ignore stale replies. Cancellation saves work, but correctness must not depend on the server receiving a cancellation in time.

05

Data model

Phase 3 of 4 · Data & async work

Connect data and background work

The services now need their dependencies: durable state, fast lookups, or work that continues outside a request. Separate these responsibilities rather than treating every component as a database.

Trie snapshots
Publish immutable snapshots and atomically switch readers to a validated version.
Query log
Collect privacy-aware query signals for aggregate popularity rather than storing unnecessary identity.
Aggregation + build
Aggregate prefix counts and periodically rebuild the serving index.
Architecture after phase 3. Includes Search box, Edge cache, Suggestion service, Rank + filter, Trie snapshots, Query log, Aggregation + build. Search box connects to Edge cache. Edge cache connects to Suggestion service. Suggestion service connects to Rank + filter. Suggestion service connects to Trie snapshots. Suggestion service connects to Query log. Query log connects to Aggregation + build. Aggregation + build connects to Trie snapshots.
Phase 3 — Added Trie snapshots, Query log, Aggregation + build. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Store normalized query candidates, display text, scores, and eligibility metadata separately from the serving index. A prefix index or trie is a derived structure that can be rebuilt. Version its snapshots so a server does not mix nodes and ranking data from different rebuilds.

06

Core design decisions

Trie Data Structure (Core)

Basic Trie Structure

Basic Trie Structure
Basic Trie StructureSVGSVGEditable diagramEditable diagram

Trie Node Structure

EXAMPLE
struct TrieNode {
    children: HashMap<char, TrieNode>,   // or array[128] for ASCII
    is_end_of_query: bool,               // marks complete query
    frequency: u64,                       // search frequency count
    top_k: Vec<(String, u64)>,           // precomputed top-K suggestions
                                          // for prefix ending at this node
}

Memory per node:
  - children pointers:  ~256 bytes (hash map) or 1 KB (array)
  - is_end + freq:      9 bytes
  - top_k (10 entries): ~400 bytes (query strings + scores)
  Total: ~700 bytes - 1.5 KB per node

For 500M unique queries × avg 20 chars:
  - Upper bound nodes: 10B (but massive sharing of prefixes)
  - Actual nodes (shared prefixes): ~2-3B
  - Memory: 2B × 1 KB = ~2 TB  ← TOO MUCH for naive trie

Optimized Trie: Compressed Trie (Patricia / Radix Tree)

Optimized Trie: Compressed Trie (Patricia / Radix Tree)
Optimized Trie: Compressed Trie (Patricia / Radix Tree)SVGSVGEditable diagramEditable diagram

Further Optimization: Top-K Precomputation

Instead of storing full queries at each node, store only the top-K suggestion IDs:

EXAMPLE
struct CompactTrieNode {
    children: Vec<(char, u32)>,     // (char, child_offset) - sparse
    top_k_ids: [u32; 10],           // indices into suggestion string pool
    flags: u8,                       // is_terminal, has_children, etc.
}

Separate string pool:
  suggestions: Vec<String>          // all 500M suggestion strings
  scores:      Vec<u32>             // corresponding popularity scores

Memory per node: ~60 bytes
Total nodes: ~500M
Total trie structure: ~30 GB  ← fits in memory!
String pool: ~10 GB
Total: ~40-50 GB per replica

Top-K Propagation Algorithm

EXAMPLE
Building the trie with top-K at each node:

1. Insert all (query, frequency) pairs into the trie
2. Bottom-up traversal:

   For each node N:
     candidates = []
     if N.is_terminal:
       candidates.append((N.query, N.frequency))
     for child in N.children:
       candidates.extend(child.top_k)
     N.top_k = heapq.nlargest(K, candidates, key=frequency)

Example:
  Node "app" has top_k:
    [("apple", 50K), ("apple pie", 30K), ("app store", 25K),
     ("apple music", 20K), ("application", 15K), ...]

Query time: O(L) where L = prefix length
  - Walk the trie character by character: O(L)
  - Return precomputed top_k: O(1)
  - Total: O(L) ← typically L < 20, essentially O(1)
07

Request flows

Query Flow (Online Serving)

Keystroke-to-Suggestion Flow

Keystroke-to-Suggestion Flow
Keystroke-to-Suggestion FlowWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Client-Side Optimizations

Client-Side Optimizations
Client-Side OptimizationsSVGSVGEditable diagramEditable diagram

Data Collection & Aggregation Pipeline

Query Log Collection

Query Log Collection
Query Log CollectionWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Frequency Scoring Formula

EXAMPLE
Score(query) = Σ  count(query, day_i) × decay(day_i)
              i=1..30

Where:
  decay(day_i) = e^(-λ × age_in_days)
  λ = 0.1 (tunable decay rate)

Example for "apple pie":
  Day 0 (today):  5000 searches × e^(0)    = 5000
  Day 1:          4800 searches × e^(-0.1) = 4342
  Day 7:          3000 searches × e^(-0.7) = 1489
  Day 30:         2000 searches × e^(-3.0) = 100
  Total score: 5000 + 4342 + ... + 100 = weighted sum

Why exponential decay?
  - Recent popularity matters more than historical
  - Trending queries rise fast, old fads decay
  - Tuning λ controls memory of the system

Trending Query Detection

Trending Query Detection
Trending Query DetectionSVGSVGEditable diagramEditable diagram

Trie Building Pipeline (Offline)

Batch Trie Construction

Batch Trie Construction
Batch Trie ConstructionWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Trie Serialization Format

Trie Serialization Format
Trie Serialization FormatSVGSVGEditable diagramEditable diagram
08

Performance & caching

Caching Strategy

Multi-Layer Cache Architecture

Multi-Layer Cache Architecture
Multi-Layer Cache ArchitectureSVGSVGEditable diagramEditable diagram

Cache Invalidation

EXAMPLE
Problem: When trie is updated, cached suggestions become stale.

Strategy: TTL-based expiration (no active invalidation)

  Justification:
  - Suggestions change slowly (hourly trie rebuilds)
  - Slightly stale suggestions are acceptable
  - Active invalidation at CDN scale is expensive and complex
  - 5-15 minute TTL = max staleness of 15 minutes

  Exception: TRENDING queries
  - Bypass CDN cache for trending prefixes
  - Set Cache-Control: no-cache for trending-eligible short prefixes
  - Or: use websocket push to update client cache for trending
09

Advanced design

Sharding & Replication

Sharding Strategy

Sharding Strategy
Sharding StrategySVGSVGEditable diagramEditable diagram

Replication

Replication
ReplicationSVGSVGEditable diagramEditable diagram

Full Serving Topology

Full Serving Topology
Full Serving TopologySVGSVGEditable diagramEditable diagram

Personalization

User History Blending

User History Blending
User History BlendingSVGSVGEditable diagramEditable diagram

User History Storage

User History Storage
User History StorageSVGSVGEditable diagramEditable diagram

Offensive Content Filtering

Multi-Layer Filtering

Multi-Layer Filtering
Multi-Layer FilteringSVGSVGEditable diagramEditable diagram

Multi-Language Support

Language-Specific Challenges

Language-Specific Challenges
Language-Specific ChallengesSVGSVGEditable diagramEditable diagram

Alternative Data Structures

Trie vs. Alternatives Comparison

Data StructurePrefix LookupMemoryBuild TimeUpdateBest For
Trie (compressed)O(L)Medium (30 GB)HoursHard (rebuild)Standard choice
Sorted array + binary searchO(L × log N)Low (10 GB)FastHardSmall corpus
Hash map (prefix → suggestions)O(1) amortizedHigh (100 GB+)FastEasyHigh QPS, small corpus
Ternary search treeO(L)Low (20 GB)HoursMediumMemory-constrained
DAWG (directed acyclic word graph)O(L)Very low (5 GB)Very slowVery hardRead-heavy, static corpus
Inverted index (prefix → docs)O(1)MediumFastEasyWhen mixing with search

Approach: Precomputed Hash Map (for high-QPS systems)

EXAMPLE
Alternative to trie for extreme QPS requirements:

Precompute ALL possible prefixes → top-K map:

  "a"     → [apple, amazon, apple pie, ...]
  "ap"    → [apple, app store, apple pie, ...]
  "app"   → [apple, app store, apple pie, ...]
  "appl"  → [apple, apple pie, apple music, ...]
  "apple" → [apple, apple pie, apple music, ...]
  ...

Storage: 500M queries × avg 20 chars = 10B prefix entries
         10B × (key + 10 suggestion IDs) = ~200 GB

  TOO MUCH for in-memory!

Optimization: Only precompute prefixes of length 1-6
  (covers ~95% of user typing before they click a suggestion)

  26^1 + 26^2 + ... + 26^6 ≈ 300M prefix entries
  But with actual query distribution: ~50M meaningful prefixes
  50M × 500 bytes = ~25 GB ← feasible!

  For prefixes > 6 chars: fall back to trie lookup

Approach: Inverted Index Style (Elasticsearch)

EXAMPLE
Instead of a trie, use an inverted index with edge n-grams:

Document: "apple pie recipe"
Index terms (edge n-grams):
  "a", "ap", "app", "appl", "apple"
  "p", "pi", "pie"
  "r", "re", "rec", "reci", "recip", "recipe"

Query: prefix "app" → lookup posting list for "app"
  → returns all suggestions containing a word starting with "app"

Advantages:
  - Leverages existing search infrastructure (Elasticsearch)
  - Easy updates (just index new documents)
  - Built-in relevance scoring, filtering, aggregations

Disadvantages:
  - Higher latency than in-memory trie (~5-10ms vs ~1ms)
  - More complex query for phrase completion
  - Overkill for simple prefix matching

Best for: Medium-scale systems (< 10K QPS) that already use Elasticsearch

Real-Time Updates (Streaming Path)

Near Real-Time Trie Update Architecture

Near Real-Time Trie Update Architecture
Near Real-Time Trie Update ArchitectureSVGSVGEditable diagramEditable diagram

Advanced Topics

Fuzzy Matching (Typo Tolerance)

EXAMPLE
Problem: User types "aple" but means "apple"

Approach 1: Edit Distance at Query Time
  For prefix "aple", find all trie nodes within edit distance 1:
    "aple" → "apple" (insert 'p')
    "aple" → "able"  (substitute p→b)
    "aple" → "ape"   (delete l)

  Cost: Expensive! For each prefix, explore O(26^d) branches
        where d = max edit distance

  Optimization: BK-tree or Levenshtein automaton
    - Precompute finite automaton for edit distance ≤ 2
    - Intersect automaton with trie in single traversal
    - O(L) time regardless of edit distance

Approach 2: Phonetic Matching
  Soundex / Metaphone encoding:
    "aple"  → A140
    "apple" → A140  (same code!)
  Store phonetic codes in separate index
  Match phonetically, then boost exact prefix matches

Approach 3: Query Log Corrections (most practical)
  When user types "aple" and then reformulates to "apple":
    Store mapping: "aple" → "apple" with confidence score
  At query time: lookup "aple" in correction map
    If found: also search for "apple" and merge results
  Low latency: O(1) hash map lookup
  High quality: based on actual user behavior

Context-Aware Suggestions

EXAMPLE
Beyond prefix matching — use context for better suggestions:

1. Previous query context:
   Query 1: "python"
   Query 2: "for" → suggest "for loop python", not "ford trucks"

2. Page context:
   User is on a cooking website
   Types "ch" → suggest "chicken recipe", not "chrome download"

3. Time context:
   During NFL season: "sup" → "super bowl" boosted
   Tax season: "h" → "h&r block", "how to file taxes" boosted

4. Device context:
   Mobile: shorter suggestions (screen space limited)
   Desktop: longer, more detailed suggestions

Implementation:
   Context vector + suggestion scoring:
   score(suggestion) = base_score
                     × context_boost(query_history)
                     × time_boost(current_events)
                     × device_factor(screen_size)

Zero-Prefix Suggestions

EXAMPLE
When user clicks search box but hasn't typed anything:

Show:
  1. User's recent searches (from personalization store)
  2. Currently trending queries
  3. Seasonal / contextual suggestions

Implementation:
  - No trie lookup needed
  - Precomputed list per user segment
  - Cached aggressively (one request per search session)
  - Updated every 15 minutes

This is the "prefetch" request — sent on search box focus
10

Edge cases

Typing, deleting, and retyping can leave several requests in flight. Display only the response that matches the current input state. Empty prefixes, composed characters, and language-specific normalization also need deliberate handling; a byte prefix is not always the same thing as the characters a person thinks they typed.

11

Tradeoffs

Key Tradeoffs

Pre-computed Top-K vs. On-the-Fly Ranking

EXAMPLE
Pre-computed (chosen for global suggestions):
  + O(L) lookup — walk trie, read pre-stored top-K
  + Extremely fast (< 1 ms)
  + Simple serving logic
  - Stale until trie rebuild (hourly)
  - Can't easily incorporate real-time personalization into top-K
  - Rebuild cost: hours of compute for 500M queries

On-the-fly ranking:
  + Always fresh
  + Easy to blend personalization, trending, context
  - Slower: must traverse subtree, score, sort at query time
  - For popular short prefixes, subtree can have millions of entries
  - Latency unpredictable (depends on subtree size)

Hybrid approach (Google's / production systems):
  Pre-compute top-K for global suggestions → fast base
  Merge with personalization + trending at query time → fresh blend
  Total cost: O(L) trie walk + O(K) merge = still very fast

Trie Rebuild Frequency

EXAMPLE
Frequent rebuilds (every 15 min):
  + Fresher suggestions
  + Trending queries appear faster
  - Higher compute cost
  - More deployment risk (more frequent swaps)
  - Diminishing returns (most queries don't change in 15 min)

Infrequent rebuilds (daily):
  + Lower compute cost
  + Stable, well-tested suggestions
  - Stale: trending topics delayed by hours
  - New queries don't appear until next rebuild

Chosen: Hourly batch rebuild + real-time overlay
  - Best of both: stable base + fresh trending
  - Overlay is small (100K-1M entries) — cheap to update
  - Merge at query time is O(K) — negligible cost

Global vs. Regional Tries

EXAMPLE
Single global trie:
  + Simpler architecture
  + One build pipeline
  + Global trends visible everywhere
  - "pizza near me" suggestions are same in Tokyo and New York
  - Wastes memory on irrelevant suggestions per region

Per-region/locale trie:
  + Region-specific suggestions ("weather tokyo" boosted in Japan)
  + Smaller per-region trie (less memory)
  + Better relevance for location-dependent queries
  - More complex build pipeline (one per locale)
  - ~50 languages × ~10 regions = 500 tries to maintain
  - Trending detection harder (smaller per-region signal)

Hybrid:
  - Global trie for universal queries (brands, tech terms)
  - Regional overlay for location-specific queries
  - Merge at query time based on user's locale/location

Memory vs. Disk for Trie Storage

FactorIn-Memory TrieDisk-Backed (mmap)
Lookup latency< 0.1 ms0.1-1 ms (cold), < 0.1 ms (warm)
Memory required50 GB per server4-8 GB (OS page cache)
Server costHigher (need large RAM)Lower (smaller instances)
Cold start timeSlow (load 50 GB)Fast (mmap, load on demand)
Suitable forHigh QPS (> 10K/server)Moderate QPS (< 5K/server)

Chosen: Memory-mapped (mmap) trie file

  • OS handles paging — hot paths stay in RAM
  • Cold paths (rare prefixes) loaded on demand from SSD
  • Best cost/performance ratio at scale
  • Cold start: instant (just mmap the file, no deserialization)

Precision vs. Recall in Suggestions

EXAMPLE
High precision (fewer, more relevant suggestions):
  + Users trust suggestions more
  + Less visual noise
  + Lower risk of offensive/irrelevant suggestions
  - May miss niche queries user actually wants
  - Less "discovery" of new queries

High recall (more diverse suggestions):
  + Users discover queries they didn't think of
  + Better for exploratory search
  - More irrelevant suggestions → user ignores autocomplete
  - Higher risk of showing offensive content
  - More visual noise

Chosen: High precision (K=5-8 suggestions)
  - Quality over quantity
  - Users want THE suggestion they're looking for
  - Offensive content risk minimized with fewer suggestions
  - Can show "more suggestions" button for users who want recall
12

Reliability & fault tolerance

Phase 4 of 4 · Operations & resilience

Add operational safeguards

With the request paths and tradeoffs established, add the controls that help detect trouble and keep the system operating. The final diagram retains every layer introduced so far.

Quality monitoring
Measure p99 response time, empty suggestions, and freshness after each index release.
Architecture after phase 4. Includes Search box, Edge cache, Suggestion service, Rank + filter, Trie snapshots, Query log, Aggregation + build, Quality monitoring. Search box connects to Edge cache. Edge cache connects to Suggestion service. Suggestion service connects to Rank + filter. Suggestion service connects to Trie snapshots. Suggestion service connects to Query log. Query log connects to Aggregation + build. Aggregation + build connects to Trie snapshots. Rank + filter connects to Quality monitoring.
Phase 4 — Added Quality monitoring. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Fault Tolerance & Reliability

Failure Scenarios & Handling

FailureImpactMitigation
Single suggestion server diesOne replica unavailableLoad balancer routes to other replicas; auto-restart
Trie build pipeline failsStale trie (hours old)Serve previous trie version; alert on build failure
Trending pipeline lagMissing trending suggestionsGracefully degrade: serve without trending boost
Personalization (Redis) downNo personalized suggestionsServe global-only suggestions; no user-visible error
CDN outageHigher load on originOrigin servers absorb load; scale up if needed
All replicas of a shard downPrefix range unavailableReturn empty suggestions for that range; partial degradation
Kafka lag (query log delay)Delayed frequency updatesTrie still serves from last build; trending may lag

Health Checking & Monitoring

Health Checking & Monitoring
Health Checking & MonitoringSVGSVGEditable diagramEditable diagram
13

Production architecture

High-Level Architecture

High-Level Architecture
High-Level ArchitectureSVGSVGEditable diagramEditable diagram

Detailed Architecture

Detailed Architecture
Detailed ArchitectureWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Serving Infrastructure

Global Deployment

Global Deployment
Global DeploymentSVGSVGEditable diagramEditable diagram

Latency Budget

Latency Budget
Latency BudgetSVGSVGEditable diagramEditable diagram
14

Further exploration

Add a new phrase that becomes popular in an hour. Decide how it enters suggestions without rebuilding the entire index, and how it later fades. Then add a removal request: safety and privacy corrections should not have to wait for the normal popularity-decay schedule.

15

Interview playbook

System Design Interview Tips

What Interviewers Look For

  1. Trie fundamentals — can you explain how a trie works and why it's the right data structure?
  2. Scale reasoning — how much data, how many requests, how much memory?
  3. Top-K at each node — this is the key insight that makes prefix lookup O(L)
  4. Data pipeline — how do you collect query frequencies and build the trie?
  5. Caching strategy — multi-layer caching is critical for 100K+ QPS
  6. Real-time trending — how do you surface breaking news quickly?
  7. Tradeoff reasoning — rebuild frequency, precision vs recall, global vs regional

Common Follow-Up Questions

QuestionKey Points
"How do you handle a new trending query?"Real-time overlay via streaming pipeline; merge with base trie at query time
"What if the trie doesn't fit in memory?"Shard by prefix range; mmap for disk-backed; keep only top-N queries
"How do you personalize without storing user data?"Client-side history in localStorage; send with request; no server storage
"How do you prevent offensive suggestions?"Multi-layer: blocklist → regex → ML classifier → human review for trending
"What happens if a user types very fast?"Client-side debouncing + request cancellation; prefix reuse for local filtering
"How do you update the trie without downtime?"Blue/green deployment: build new trie → load into standby → swap pointer

Suggested 45-Minute Interview Structure

EXAMPLE
 0-5  min:  Clarify requirements (search suggestions? K=? latency?)
 5-10 min:  Scale estimations (QPS, corpus size, memory)
10-18 min:  Core data structure (trie, compressed trie, top-K precomputation)
18-28 min:  System architecture (serving path, data pipeline, caching)
28-35 min:  Deep dive: pick ONE
            - Option A: Real-time trending pipeline
            - Option B: Sharding & replication
            - Option C: Personalization strategy
35-42 min:  Tradeoffs (rebuild frequency, trie vs alternatives, global vs regional)
42-45 min:  Monitoring, failure handling, fuzzy matching