ESC

Type to search the knowledge base.

Design Autocomplete / Typeahead

Frontend system design for search typeahead — client UX, API, caching, ranking surface, and a11y at scale.

intermediate4 min read
  • system-design
  • search
  • performance
  • caching
  • interview

Scope the problem

Autocomplete interviews span UI component and system design. Clarify which hat they want; usually it’s frontend-heavy with API contracts and light backend awareness.

In scope:

  • Query input + suggestion dropdown
  • Debounce, cancelation, keyboard UX, a11y
  • Client cache and request coalescing
  • API shape, pagination of suggestions, empty/error
  • Personalization hooks (recent, trending) at a high level
  • Latency budget and ranking surface (not ML training)

Out of scope unless asked:

  • Full search index sharding
  • Spell-correction model training
  • Multi-region failover diagrams for 20 minutes

Scale assumptions:

  • Millions of queries/day; p95 suggest latency < 100–150ms edge-to-client ideal
  • Mobile keyboards; flaky networks
  • Multi-entity results (people, pages, queries)

Requirements

Type Detail
Functional Suggest as you type; select navigates or fills; recent searches
Non-functional Low input lag, race-safe results, accessible combobox
Privacy Don’t leak queries to third parties; careful logging

Product questions to ask: search-as-you-type vs submit-only? Multi-select tokens? Vertical-specific (e.g. maps places)?

High-level architecture

┌─────────────┐     debounced q      ┌──────────────────┐
│  Combobox   │ ───────────────────► │  Suggest API     │
│  UI + cache │ ◄─────────────────── │  (edge / BFF)    │
└─────────────┘   ranked suggestions └────────┬─────────┘
                                              │
                                   ┌──────────▼──────────┐
                                   │ Prefix index /      │
                                   │ search service      │
                                   └─────────────────────┘

Frontend owns: UX state machine, caching, abort, rendering, analytics. Backend owns: index, ranking, authz for private entities.

Client component design

Mirror the machine-coding brief: Autocomplete Search Box.

Typeahead
├── Input (combobox)
├── Panel (listbox)
│   ├── Section: Recent
│   ├── Section: Suggestions
│   └── Section: Trending (optional)
└── Status (loading / empty / error)

State machine (simplified)

idle → typing → debouncing → loading → results | empty | error
with transitions for select, escape, blur (careful: blur vs option mousedown).

Client API

type SuggestRequest = {
  q: string;
  limit?: number;
  lang?: string;
  sessionId?: string; // personalization without always sending user id
};

type SuggestItem = {
  id: string;
  type: "query" | "user" | "page" | "place";
  label: string;
  sublabel?: string;
  score?: number;
};

type SuggestResponse = {
  items: SuggestItem[];
  requestId: string;
};

Latency & correctness (frontend)

Technique Why
Debounce 150–300ms Cut QPS; feel still snappy
Min chars (1–2) Avoid useless traffic
AbortController Stale responses never paint
In-flight map by query Coalesce identical concurrent
LRU cache of responses Backspace feels instant
Prefetch on hover of global search icon Optional warm-up

Race rule: only apply results if response.q === latestQuery (or abort). This is a non-negotiable talking point.

Caching strategy

Layer Key TTL / policy
Memory LRU exact q + locale Session; capacity ~50–100
HTTP cache GET /suggest?q= Short max-age or private no-store if personalized
Persistent recent user selections Local only; capped

Personalized suggests: prefer Cache-Control: private and client memory over shared CDN cache.

Ranking surface (what FE needs)

Discuss as a product API, not ML:

  1. Prefix / n-gram text match
  2. Popularity prior
  3. User recent + graph affinity
  4. Entity type boosts (people first in a people search)

UI may group by type; don’t re-sort server ranking arbitrarily unless product requires.

Accessibility & i18n

  • Combobox APG pattern: aria-expanded, aria-activedescendant, listbox options
  • Highlight matches without breaking screen reader labels
  • RTL layout for panel
  • IME composition: don’t debounce-fire mid-composition
  • Announce result count via polite live region

Performance budgets

  • Input handler work < few ms; no heavy JSON parse on main thread for huge payloads (limit server-side)
  • Panel render: virtualize only if 50+ options (usually 5–10)
  • Bundle: keep typeahead in the header chunk or lazy when search is secondary

Observability

  • Client: time-to-first-suggest, abandon rate, select-through rate, error rate
  • Correlate with requestId
  • Watch for “empty rate” spikes after deploys

Tradeoffs

  1. Client filter of static list vs server suggest — client only for tiny dictionaries
  2. Aggressive cache vs freshness of trending
  3. SSE/WebSocket push of trending vs pull
  4. Native datalist vs custom combobox — datalist fails most product UX
  5. BFF aggregation of multi-vertical search vs parallel FE calls

Interview delivery (45 min)

  1. 0–5m Scope, users, latency goals
  2. 5–15m UI architecture + a11y
  3. 15–25m API + race/debounce/cache
  4. 25–35m Backend sketch + ranking surface
  5. 35–45m Perf, privacy, tradeoffs, metrics

Further reading