Search Autocomplete System Design Template (Reference Design)
Typeahead: trie/FST/edge n-gram index, popularity and recency ranking, query-log pipeline, edge cache, latency budget, safety
How to use: Use when adding search suggestions. Start from the latency budget and size the index from distinct queries above a threshold. The main pitfall is serving harmful or personal suggestions; filter in the pipeline and again at serve time.
Preview
System Design: [Search autocomplete name]
Reference design for search typeahead (autocomplete): as the user types, the service returns the most useful completions for the prefix within tens of milliseconds. It covers prefix indexes (trie, finite state transducer, edge n-grams), ranking by popularity and recency, the data pipeline from query logs, edge caching, the latency budget, personalisation and abuse filtering. It follows the structure of the System Design Document template and is pre-filled with a worked design to adapt. Values marked Example are assumptions: replace them and recompute.
Document control
| Field | Value |
|---|---|
| Document ID | [SDD-AC-001] |
| System / feature | [Search suggestions] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Implemented / Superseded] |
| Author(s) | [@author] |
| Reviewers | [@reviewer-1], [@reviewer-2] |
| Last updated | YYYY-MM-DD |
| Related | [Search PRD], [Content policy for suggestions], [Privacy review] |
Approvals
| Role | Name | Decision | Date |
|---|---|---|---|
| Tech lead | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Staff / principal engineer | [@name] | [Decision] | YYYY-MM-DD |
| SRE / operations | [@name] | [Decision] | YYYY-MM-DD |
| Trust and safety / policy | [@name] | [Decision] | YYYY-MM-DD |
| Privacy | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft for design review |
| 0.2 | YYYY-MM-DD | [@name] | Example: Added trending layer with 5-minute windows |
| 0.3 | YYYY-MM-DD | [@name] | Example: Moved personal history to the device by default |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Overview
1.1 Problem statement
[Example: Today suggestions come from a LIKE 'prefix%' query on the
product database. p99 is 380 ms, suggestions are alphabetical rather than
useful, breaking news terms take a day to appear, and last month an offensive
suggestion was screenshotted and shared widely.]
1.2 Proposed solution
[Example: An offline pipeline aggregates anonymised query logs hourly into scored suggestions, filters them against policy lists, and builds an immutable in-memory prefix index (a trie with the top completions cached at each node). Suggest servers load the index and answer prefix requests in under 5 ms. A streaming layer adds trending queries within minutes. Responses for short, popular prefixes are cached at the CDN edge for 5 minutes. Personal recent searches are blended in on the client from local history.]
1.3 Decisions requested from reviewers
| ID | Question | Recommended option | Section |
|---|---|---|---|
| Q-01 | Example: Trie with top-k per node, FST, or search-engine edge n-grams? | Trie with top-k per node | 7.1, 10 |
| Q-02 | Example: How fresh must suggestions be? | Hourly base index + 5-minute trending layer | 7.3 |
| Q-03 | Example: Server-side personal history? | No by default; device-local history, opt-in sync | 7.5 |
| Q-04 | Example: Cache at edge for which prefixes? | Length 1 to 3, non-personalised responses only | 7.4 |
| [Q-nn] | [Question] | [Option] | [Section] |
2. Requirements
2.1 Functional requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Example: Return up to 10 completions for a prefix, ranked by usefulness | Must |
| FR-02 | Example: Support per-locale suggestions (language and market) | Must |
| FR-03 | Example: Reflect trending queries within 15 minutes | Should |
| FR-04 | Example: Never suggest terms on the policy blocklist | Must |
| FR-05 | Example: Blend the user's recent searches at the top, removable by the user | Should |
| FR-06 | Example: Tolerate one typo for prefixes of 4+ characters | Could |
| FR-07 | Example: Remove a reported suggestion globally within 10 minutes | Must |
| [FR-nn] | [Requirement] | [Must / Should / Could] |
2.2 Non-functional requirements
| ID | Quality | Target | Measured as |
|---|---|---|---|
| NFR-01 | Latency | Example: Server p99 at or below 20 ms; perceived p95 at or below 100 ms | Server metrics, client RUM |
| NFR-02 | Availability | Example: 99.9%; search must work without suggestions | Synthetic checks |
| NFR-03 | Freshness | Example: Base index at most 2 h old; trending at most 15 min | Index build timestamp |
| NFR-04 | Safety | Example: Zero blocklisted suggestions served | Audit sampling of served suggestions |
| NFR-05 | Privacy | Example: No suggestion derived from fewer than 50 distinct users | Pipeline threshold test |
| [NFR-nn] | [Quality] | [Target] | [Measurement] |
2.3 Out of scope
- [Example: the search results page itself]
- [Example: rich entity cards in the dropdown]
- [Example: voice query suggestions]
- [Item deferred, with the release that will take it]
3. Capacity estimation
3.1 Assumptions
| Input | Symbol | Value | Source |
|---|---|---|---|
| Daily active searchers | Example: 100 million | Analytics | |
| Searches per user per day | Example: 5 | Analytics | |
| Suggest requests per search after 100 ms debounce | Example: 4 | Client telemetry | |
| Peak to average factor | Example: 3 | Traffic history | |
| Edge cache hit ratio | Example: 60% | Pilot measurement | |
| Distinct suggestions kept (per locale set) | Example: 20 million | Log analysis above threshold | |
| Average suggestion length | Example: 20 characters | Log analysis | |
| Distinct prefix nodes as share of total characters | Example: 0.3 | Trie build on a sample |
3.2 Request rate
Origin load after the edge cache:
3.3 Index size
Prefix nodes:
With a top-10 list of 4-byte suggestion IDs (40 B) plus about 24 B of node structure:
plus suggestion strings and scores ( B GB), about 8.3 GB per replica. Each suggest server holds a full replica, so no cross-node calls are needed on the request path.
3.4 Pipeline volume
3.5 Summary
| Quantity | Estimate | Design consequence |
|---|---|---|
| Requests | ~69 000/s peak, ~28 000/s at origin | Small fleet of in-memory servers behind edge cache |
| Index | ~8.3 GB per replica | Full replica per server; no sharding needed yet |
| Log volume | ~100 GB/day | Hourly batch aggregation is cheap |
| Latency | 20 ms server budget | No disk or network hop on the hot path |
4. API design
4.1 Endpoints
| Method | Path | Purpose | Auth | Cacheable |
|---|---|---|---|---|
| GET | /v1/suggest?q={prefix}&locale={loc}&n=10 | Suggestions for a prefix | None or session | Yes, for anonymous requests |
| POST | /v1/suggest/feedback | Report a suggestion | Session | No |
| POST | /internal/v1/blocklist | Add a term or pattern (policy team) | Admin role | No |
4.2 Request and response examples
The prefix is normalised (case-folded, whitespace collapsed) on the client and the server so cache keys match:
GET /v1/suggest?q=wint&locale=en-GB&n=8 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=300
Vary: Accept-Encoding
{"q": "wint", "indexVersion": "2026-03-14T09", "suggestions": [{"text": "winter coats", "kind": "query"}, {"text": "winter tyres", "kind": "query"}, {"text": "winter olympics schedule", "kind": "trending"}]}
4.3 Errors and limits
| Status | Problem type | When |
|---|---|---|
| 400 | prefix-too-long | Prefix over 100 characters |
| 429 | rate-limited | Client over 20 requests/s; Retry-After set |
Clients treat any error or a response slower than 300 ms as "no suggestions"; typing is never blocked.
5. Data model
5.1 Entities
The pipeline turns raw query events into scored suggestions; an index build packages them for serving.
5.2 Storage choices
| Data | Store | Key | Main queries | Why |
|---|---|---|---|---|
| Query events | Event stream, then data lake (partitioned by hour) | Time | Batch aggregation | Cheap, append-only |
| Query stats | Data warehouse table | Query, locale, day | Score computation | SQL-friendly batch |
| Index builds | Object storage, versioned files | version | Download by servers | Immutable artifacts, easy rollback |
| Trending counters | In-memory stream processor state | Query, locale, window | Top-k per window | Minutes-level freshness |
| Block rules | Relational database, replicated to servers | rule_id | All active rules | Audited, small |
6. High-level design
The serving path is read-only and in memory; all heavy work happens in the pipeline.
| Component | Responsibility | Scales by |
|---|---|---|
| Suggest servers | Look up prefix, merge trending, filter, return top N | Replicas (each holds full index) |
| Stream processor | Count queries per 5-minute window, detect spikes | Partitions by query hash |
| Aggregation and scoring | Compute decayed popularity per query and locale | Batch cluster |
| Policy filter | Apply block rules, privacy thresholds | Batch step |
| Index builder | Build trie with top-k per node, validate, publish | Single batch job per locale group |
| [Component] | [Responsibility] | [Scaling unit] |
7. Detailed design
7.1 Prefix index options
| Structure | Lookup | Memory | Updates | Fit |
|---|---|---|---|---|
| Trie with top-k cached per node | O(prefix length) then read list | Largest | Rebuild or careful in-place | Chosen: fastest, simple serving |
| Finite state transducer (FST) | O(prefix length) plus traversal for top-k | Smallest (shares prefixes and suffixes) | Immutable, rebuild | Good when memory bound; top-k needs weighted traversal |
| Edge n-grams in a search engine | Inverted index query | Large (every prefix indexed) | Near real time | Good when suggestions need full-text features and filters |
| Sorted array + binary search | O(log N) then scan | Small | Rebuild | Simple baseline; top-k needs scan of range |
The trie stores, at each node, the IDs of the 10 highest-scoring suggestions in its subtree, computed bottom-up during the build. A lookup walks the prefix and returns that list without exploring the subtree.
7.2 Ranking
Base score for query in locale uses exponentially decayed daily counts with half-life (Example: 7 days):
At serve time, the trending layer adds a boost when the last window greatly exceeds the expected rate:
where is the average 5-minute count over the last 7 days. A query seen 16 times more than usual gets a boost of about 4 score units; the weight of the boost against is tuned with online experiments. Final order also applies diversity (no more than 3 suggestions sharing the same first two words).
7.3 Data pipeline
| Stage | Frequency | Work | Output |
|---|---|---|---|
| Ingest | Continuous | Normalise query, drop events from known bots, hash user ID with rotating salt | Events in lake and stream |
| Aggregate | Hourly | Count per query, locale, day; count distinct users | Query stats |
| Threshold | Hourly | Keep queries with at least 50 distinct users in 30 days | Candidate suggestions |
| Filter | Hourly | Apply block rules, PII detectors (emails, phone numbers, ID numbers) | Clean suggestions |
| Build | Hourly | Build trie per locale group, compute checksums, run golden-prefix tests | Index version |
| Publish | Hourly | Upload; servers download, load into memory, swap atomically | Serving index |
Servers keep the previous version in memory until the new one passes a self-test, so a bad build never serves.
7.4 Suggest request (critical path) and caching
Short prefixes are the most requested and the least personal, so they are cached at the edge; longer prefixes go to origin.
Clients also keep a small in-memory cache: if "win" returned 10 results and the user types "wint", the client can filter locally while the request is in flight.
7.5 Personalisation
Personal recent searches stay on the device (local storage, last 50 entries, user-clearable) and are blended on the client by prefix match, which keeps edge responses cacheable and avoids server-side profiles. Signed-in users may opt in to sync history; synced history is encrypted at rest, deletable, and never used to train global suggestions.
7.6 Abuse and safety filtering
- Block rules: exact terms, patterns and entity lists maintained by the policy team; applied in the pipeline and again at serve time so a new rule takes effect within the 10-minute rule-sync interval without a rebuild.
- Manipulation: coordinated query injection is limited by the distinct-user threshold, bot filtering and caps on the trending boost per source network.
- Privacy: the threshold and PII detectors prevent rare personal queries (names with addresses, account numbers) from becoming suggestions.
7.7 Consistency model
All servers converge on the same index version within one publish cycle; during rollout two versions may serve for up to 10 minutes, which users cannot notice. Block-rule removals are the only change with a hard deadline (FR-07) and are pushed separately from index builds.
8. Scalability and performance
| Concern | Approach | Limit before redesign |
|---|---|---|
| Request rate | Stateless replicas, edge cache | Example: ~5 000 req/s per server, so 6 servers for peak origin load plus 2 spare |
| Index memory | Full replica per server | When index exceeds ~50% of server memory, shard by locale group |
| Build time | Parallel per locale group | Hourly cadence needs builds under 20 min |
| Long-tail prefixes | Served from origin; edge only for short ones | [Limit] |
Latency budget, keystroke to rendered suggestions (target p95 100 ms):
| Step | Budget |
|---|---|
| Debounce | 0 ms after last pause (not counted) |
| Network to edge (round trip) | 30 ms |
| Edge to origin on miss (round trip) | 25 ms |
| Server processing (lookup, merge, filter) | 5 ms |
| Client blend and render | 10 ms |
| Headroom | 30 ms |
9. Reliability, security and operations
9.1 Failure modes
| Failure | Impact | Detection | Mitigation |
|---|---|---|---|
| Bad index build | Wrong or empty suggestions | Golden-prefix tests, size and checksum checks | Build rejected; servers keep previous version |
| Pipeline delayed | Stale suggestions | Index age metric | Serve last good index; trending layer still fresh |
| Trending stream down | No trending boosts | Window lag metric | Serve base index only |
| Edge cache poisoning by personal data | Leak of one user's suggestions | Response audit | Only anonymous, non-personalised responses are cacheable (private otherwise) |
| Suggest servers down | No suggestions | Synthetic checks | Search box works without them; client timeout 300 ms |
| [Failure] | [Impact] | [Signal] | [Mitigation] |
9.2 Security, privacy and abuse
| Threat | Control |
|---|---|
| Offensive or defamatory suggestions | Block rules, reporting with 10-minute takedown, human review queue |
| Suggestion manipulation by bots | Distinct-user threshold, bot filtering, trending caps |
| Personal data in suggestions | Threshold of 50 users, PII detectors, hashed user IDs with rotating salt |
| Scraping of the suggestion corpus | Rate limits per IP; corpus is low sensitivity by design |
| Retention of query logs | Example: Raw events 30 days, aggregates 13 months |
| [Threat] | [Control] |
9.3 Observability
| Signal | Metric or log | Alert threshold |
|---|---|---|
| Server latency | p99 | Above 20 ms for 10 min |
| Perceived latency | Client RUM p95 | Above 100 ms for 30 min |
| Index age | Now minus build time | Above 2 h |
| Suggestion acceptance | Share of searches that used a suggestion | Drop of 10% vs. baseline |
| Safety | Reports per million impressions | Spike above 3x baseline |
9.4 Cost estimate
| Item | Unit cost | Quantity | Monthly cost |
|---|---|---|---|
| Suggest servers (32 GB RAM) | [$/node-month] | Example: 12 per region | [$] |
| CDN requests | [$/million] | Example: ~1.2 billion edge hits/day | [$] |
| Pipeline compute | [$/hour] | 24 builds/day | [$] |
| Data lake storage | [$/TB-month] | Example: 3 TB (30 days raw) | [$] |
| Total | [$] |
10. Trade-offs and alternatives
| Decision | Options | Chosen | Why | What we give up |
|---|---|---|---|---|
| Index structure | Trie top-k; FST; edge n-grams in search engine | Trie top-k | Constant-time top-k, simplest hot path | More memory than FST |
| Freshness | Real-time index updates; hourly rebuild + trending layer | Hourly + trending | Immutable builds are safe and testable; trending covers news | Two layers to merge |
| Personalisation | Server profiles; device-local history | Device-local | Privacy, edge cacheability | No cross-device history without opt-in |
| Caching | No edge cache; cache all; cache short prefixes | Short prefixes | Most traffic at lowest cost | Long prefixes always hit origin |
| Typo tolerance | None; fuzzy trie traversal; separate spell index | Deferred (Could) | Complexity versus small gain at launch | Fewer results for misspellings |
| [Decision] | [Options] | [Chosen] | [Why] | [Cost] |
11. Delivery plan and risks
11.1 Milestones
| Milestone | Scope | Exit criterion | Target date |
|---|---|---|---|
| M1 | Pipeline and daily index, one locale | Golden-prefix tests pass; p99 under 20 ms | YYYY-MM-DD |
| M2 | Hourly builds, edge cache, all locales | Acceptance rate up vs. old system in A/B | YYYY-MM-DD |
| M3 | Trending layer and reporting flow | Trending visible within 15 min; takedown under 10 min | YYYY-MM-DD |
| M4 | Device-local personal history | Privacy review signed | YYYY-MM-DD |
11.2 Rollout and rollback
[Example: A/B test against the old endpoint for 2 weeks per locale. Rollback flips the client config back to the old endpoint; index rollback pins servers to a previous version.]
11.3 Risks
| ID | Risk | Likelihood | Impact | Mitigation | Owner |
|---|---|---|---|---|---|
| R-01 | Example: Harmful suggestion goes viral | Low | High | Dual filtering, fast takedown, on-call policy contact | [@name] |
| R-02 | Example: Index outgrows server memory | Medium | Medium | Shard by locale group; FST evaluation ready | [@name] |
| R-03 | Example: Trending boost amplifies manipulation | Medium | Medium | Caps per source network, human review of top trending | [@name] |
| [R-nn] | [Risk] | [L / M / H] | [L / M / H] | [Mitigation] | [@name] |
12. Appendices
12.1 Open questions
| ID | Question | Owner | Due |
|---|---|---|---|
| OQ-01 | Example: Do we suggest product entities as well as queries? | [@name] | YYYY-MM-DD |
| OQ-02 | Example: Which locales share an index? | [@name] | YYYY-MM-DD |
| [OQ-nn] | [Question] | [@name] | YYYY-MM-DD |
12.2 Glossary
| Term | Meaning |
|---|---|
| Prefix | Characters typed so far |
| Trie | Tree keyed by characters where each path is a prefix |
| FST | Finite state transducer: compact automaton mapping strings to values |
| Edge n-gram | Indexing every leading substring of a term |
| Debounce | Waiting for a short pause in typing before sending a request |
| [Term] | [Meaning] |
12.3 References
| Reference | Use |
|---|---|
| RFC 9111 HTTP Caching | Edge caching rules in 7.4 |
| RFC 9110 HTTP Semantics | API and Retry-After |
| [Search engine documentation on completion suggesters] | Alternative in 7.1 |
| [Internal content policy for suggestions] | Filtering in 7.6 |
Standard and licence note
Based on common industry practice for search autocomplete design and on the HTTP specifications cited in 12.3. This is a reference design to adapt, with illustrative numbers. Original text written for this collection. Template licence: MIT.
Related templates
Database Design Document Template (ERD and Physical Model)
Database design: DBMS choice, conceptual, logical and physical models, indexes, partitioning, integrity, security, backup, capacity
Architecture & System DesignChat System Design Template (Reference Design)
Real-time 1:1 and group chat: WebSocket gateways, per-conversation ordering, fan-out, receipts, offline sync, push, E2EE
Architecture & System DesignCollaborative Editing System Design Template (Reference Design)
Real-time co-editing: OT vs CRDT, sequence CRDT, state-vector sync, presence, offline merge, snapshots and compaction, access