•CASE STUDY

Typeahead / Search Autocomplete

7 min read·1,233 words·Intermediate

Asked at

5 candidate reports between Nov 2025 and Aug 2026

How to use this case study

SDE-2 / Mid

  • Explain the trie with top-K suggestions stored at each node
  • How a prefix lookup returns results fast

SDE-3 / Senior

  • Go deeper on building and updating the index from query logs
  • Sharding by prefix
  • Caching at the client and CDN
  • Ranking with freshness

Staff / Principal

  • Discuss personalization and trending queries within minutes
  • Multi-language support
  • Filtering offensive suggestions
  • Keeping p99 latency under 100 ms at huge scale

Problem RestatementProblem

Design the autocomplete box in a search bar. As the user types "how to", we show the top ~10 suggestions such as "how to tie a tie" and "how to make pancakes", updating on every keystroke. Suggestions come from what people search most, should include trending searches within minutes, and must appear in under 100 ms per keystroke.

RequirementsRequirements

1.1 Functional

  • Return the top 10 suggestions for a prefix.
  • Rank by popularity, with freshness (trending) and optionally personalization.
  • Update suggestions as new searches happen.
  • Hide offensive or blocked suggestions.

1.2 Non-Functional

  • Very low latency: under 100 ms end to end, ideally about 10 ms on the server.
  • Very high QPS: every keystroke is a request.
  • Highly available. It's fine if suggestions are a few minutes stale.

1.3 Scale Estimates

  • 500M searches/day, ~10 keystrokes each → 5B suggestion requests/day ≈ 60K/sec, peak ~200K/sec.
  • Distinct queries worth suggesting: ~100M. At ~50 bytes each plus trie overhead, that's tens of GB. This can be sharded, and the most popular prefixes are small.

1.4 API Design

  • GET /v1/suggest?q=how%20to&limit=10&lang=en → ["how to tie a tie", "how to make pancakes", ...]

High-Level ArchitectureArchitecture

2.1 Overview

  • Suggestion Service: looks up the prefix in an in-memory index and returns the top 10.
  • Trie index: built offline and loaded into memory on suggestion servers.
  • Query log pipeline: every completed search goes to Kafka. A batch job counts queries (e.g., over the last 7 days, weighted toward recent days) and builds a new trie. A streaming job tracks trending queries.
  • Caches: the browser caches recent results. The CDN or edge caches very common short prefixes ("a", "ho", "how").

2.2 Architecture Diagram

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
    U["Search box"] -->|"prefix"| CDN["CDN / edge cache"]
    CDN --> SS["Suggestion Service - in-memory trie"]
    U -->|"completed searches"| K[("Kafka - query log")]
    K --> BJ["Daily batch - count, build trie"]
    K --> ST["Streaming - trending counts"]
    BJ --> OS[("Trie snapshots")]
    OS -->|"load"| SS
    ST -->|"trending boost"| SS

Data Structure: Trie with Top-K

A trie (prefix tree) stores strings letter by letter: the path h → o → w represents "how". To make lookups instant:

  • At each node, store the top 10 completions for that prefix, already sorted.
  • A lookup walks down the prefix (length L, e.g., 6 steps) and returns the stored list. No searching the subtree.
  • Memory trade-off: we store top-10 lists at every node, which is more memory but constant-time answers.

A simpler alternative: a sorted list of all queries, where a binary search finds the range with that prefix. That still needs top-K, so we precompute it for short prefixes.

Key FlowsFlows

4.1 Serving a keystroke

  1. The client waits ~50 ms after typing stops (debounce) to avoid sending a request for every fast keystroke.
  2. It checks its local cache (results for "how t" are often reused).
  3. The edge cache serves common prefixes. Otherwise, the suggestion server walks the trie and returns the top 10.
  4. The server optionally mixes in trending and personal suggestions (recent searches by this user).

4.2 Building the index

  1. A daily job counts all queries from the logs, with weights so recent days count more.
  2. It removes blocked and unsafe queries and applies minimum-count thresholds (rare queries may contain private info).
  3. It builds the trie with top-K lists, writes a snapshot, and servers load it (blue/green: load the new one, then switch).

Deep Dive A — Suggesting something that only started happening an hour agoDeep dive

The trie is built from a query log and shipped to the serving fleet. News breaks, everyone searches a name nobody searched yesterday, and the box has never heard of it.

Weak

Rebuild the trie every night

A batch job counts yesterday's queries, builds the structure, and pushes it to the servers at 3 AM.

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  LOG[("Yesterday's query log")] --> B["Nightly build"]
  B --> T["Trie shipped at 3 AM"]
  T --> S["Serving fleet"]
  NEWS["Story breaks at 10 AM"] --> GAP["No suggestion until 3 AM tomorrow"]

Up to 24 hours of staleness on exactly the queries with the most volume. Users type the full phrase by hand, which is the one thing autocomplete exists to prevent.

Good

Rebuild more often

Run the job hourly instead of nightly. Staleness drops from a day to an hour.

The cost scales with the frequency: each build re-counts the whole log, rebuilds a structure of tens of gigabytes, and pushes it to every server. Running it hourly means the fleet is constantly absorbing full index pushes, and an hour is still far too slow for a story that peaks in twenty minutes. Rebuilding faster is spending more to remain late.

Deep Dive B — Scale and shardingScale

  • Replicate full tries across many servers if they fit in memory (~tens of GB is fine on large machines).
  • If too big, shard by prefix: "a–c" on shard 1, and so on. Popular first letters (like "s") need smaller ranges. A router maps the first 1–2 characters to a shard.
  • Deterministic ties: when two suggestions have equal scores, sort alphabetically so results don't flicker between keystrokes.
  • Personalization: keep each user's recent searches on the client or in a small per-user store, and blend them in at the top.

Trade-offs & AlternativesTrade-offs

DecisionChoiceWhyAlternative
IndexTrie with top-K per nodeO(prefix length) lookupsCompute top-K on each request: too slow
UpdatesDaily rebuild + streaming trendingStable and freshUpdate the trie on every search: complex locking, costly
ServingIn-memory, replicatedMicrosecond lookupsDatabase queries: too slow per keystroke
ClientDebounce + local cacheCuts requests by more than halfRequest on every key: wasteful

Common Follow-up QuestionsFollow-ups

  • "How do you handle typos?" Add a fuzzy-matching fallback (edit distance of 1) for when the prefix has no results, or learn common misspellings from logs.
  • "Privacy?" Only suggest queries typed by many distinct users (e.g., 50+), so personal data never shows up as a suggestion.
  • "Other languages?" Build a separate trie per language or market, and normalize text (lowercase, remove accents) before lookup.

Wrap-UpWrap-up

Keep an in-memory trie where each node stores its precomputed top 10 completions, so every keystroke is a short walk down the tree. Rebuild it daily from query logs with filters, blend in a streaming trending index for freshness, and cut load with client debounce and caching plus edge caching of short prefixes. Replicate the trie, or shard it by prefix when it grows too big.

More Case Studies

Frequently Asked Questions

What is the Typeahead / Search Autocomplete system design question?

Typeahead / Search Autocomplete is a system design interview question asked at FAANG companies. It covers search, caching, data pipelines, algorithms and tests your ability to design scalable, production-ready systems. InterviewSkool's breakdown walks you through requirements, API design, architecture, and trade-offs.

Which companies ask the Typeahead / Search Autocomplete question?

Microsoft, OpenAI, Pinterest have reportedly asked variations of this question in system design interviews. The exact wording may differ, but the core design challenges remain the same.

How should I prepare for the Typeahead / Search Autocomplete interview question?

Start with the problem statement and scale estimates, then design the high-level architecture. Focus on the core components, data model, and API design. InterviewSkool's breakdown covers the full solution with mermaid diagrams and trade-off analysis to help you prep efficiently.

What level is the Typeahead / Search Autocomplete question?

This question is suitable for SDE-2, SDE-3, and Staff engineer interviews. The level guidance on this page provides specific tips for each level — SDE-2 candidates should focus on core architecture, while Staff engineers should discuss trade-offs, monitoring, and incremental rollouts.

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with InterviewSkool's AI interviewer.

Start System Design Interview →