Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.

← System Design Interview Playbook

Interview Framework

  • The 6-Step Design Framework
  • HLD Fundamentals Refresher
  • Requirement Gathering Practice
  • Domain Decomposition
  • API Contract Design
  • Data Ownership Model
  • Failure Scenario Walkthroughs
  • Architecture Diagramming
  • Back-of-Envelope Estimation

10 Case Studies

  • Design a URL Shortener
  • Design Twitter / X
  • Design WhatsApp
  • Design Netflix
  • Design a Rate Limiter
  • Design a Search Autocomplete
  • Design a Distributed Cache
  • Design a Notification Service
  • Design Uber / Ride Sharing
  • Design a Distributed File Storage System
  • Design a Distributed Task Scheduler
  • Design a Message Queue System
  • Design an Authentication System at Scale
  • Design a Distributed Logging & Metrics Pipeline
  • Design a Food Delivery Platform
  • Design a Real-Time Analytics Dashboard
  • Design a Monitoring & Alerting System
  • Design Container Orchestration Basics
  • Design a CI/CD Pipeline System
  • Design Service Mesh Basics
  • Design a Centralized Configuration & Secrets System
  • Design a Batch Processing System
  • Design a Data Warehouse / Analytics Storage Layer
  • Design Global Content Delivery
  • Case studies

    🏗️Design a URL Shortener
  • 🏗️Design a Rate Limiter
  • 🏗️Design Twitter / X
  • 🏗️Design WhatsApp
  • 🏗️Design Netflix
  • 🏗️Design a Distributed Cache
  • 🏗️Design a Notification Service
  • 🏗️Design a Search Autocomplete System
  • 🏗️Design Uber / Ride Sharing
  • 🏗️Design a Web Crawler
  • 🏗️Design a Payment System
  • 🏗️Design a Distributed Lock Service
  • 🏗️Design a Video Streaming Platform
  • 🏗️Design a Search Engine
  • 🏗️Design E-Commerce Checkout & Inventory at Scale
Chaturmind
← System Design Interview Playbook

Interview Framework

  • The 6-Step Design Framework
  • HLD Fundamentals Refresher
  • Requirement Gathering Practice
  • Domain Decomposition
  • API Contract Design
  • Data Ownership Model
  • Failure Scenario Walkthroughs
  • Architecture Diagramming
  • Back-of-Envelope Estimation

10 Case Studies

  • Design a URL Shortener
  • Design Twitter / X
  • Design WhatsApp
  • Design Netflix
  • Design a Rate Limiter
  • Design a Search Autocomplete
  • Design a Distributed Cache
  • Design a Notification Service
  • Design Uber / Ride Sharing
  • Design a Distributed File Storage System
  • Design a Distributed Task Scheduler
  • Design a Message Queue System
  • Design an Authentication System at Scale
  • Design a Distributed Logging & Metrics Pipeline
  • Design a Food Delivery Platform
  • Design a Real-Time Analytics Dashboard
  • Design a Monitoring & Alerting System
  • Design Container Orchestration Basics
  • Design a CI/CD Pipeline System
  • Design Service Mesh Basics
  • Design a Centralized Configuration & Secrets System
  • Design a Batch Processing System
  • Design a Data Warehouse / Analytics Storage Layer
  • Design Global Content Delivery
  • Case studies

    🏗️Design a URL Shortener
  • 🏗️Design a Rate Limiter
  • 🏗️Design Twitter / X
  • 🏗️Design WhatsApp
  • 🏗️Design Netflix
  • 🏗️Design a Distributed Cache
  • 🏗️Design a Notification Service
  • 🏗️Design a Search Autocomplete System
  • 🏗️Design Uber / Ride Sharing
  • 🏗️Design a Web Crawler
  • 🏗️Design a Payment System
  • 🏗️Design a Distributed Lock Service
  • 🏗️Design a Video Streaming Platform
  • 🏗️Design a Search Engine
  • 🏗️Design E-Commerce Checkout & Inventory at Scale
HomeLearnSystem Design Interview Playbook10 Case Studies
Search Autocomplete

Design a Search Autocomplete System

triecachingredisreal-time

Problem Statement

Design a real-time search autocomplete feature like Google's search bar. As the user types, show the top 5 matching suggestions within 100ms. Suggestions should be ranked by query popularity.

Requirements

Functional

  • ✓Show top 5 suggestions as user types (each keystroke)
  • ✓Suggestions ranked by global query frequency
  • ✓Support prefix matching (type 'jav' → 'java', 'javascript', 'java interview')
  • ✓Personalised suggestions based on user history (optional)
  • ✓Handle 10 character minimum prefix filtering

Non-Functional

  • ✓100M DAU, 10 keystrokes per search
  • ✓Response latency < 100ms
  • ✓1B queries/day → suggestions updated in near-real-time
  • ✓Tolerant of slightly stale suggestions (1 hour stale is OK)

Capacity Estimation

Capacity Estimation

  • Read QPS: 100M users × 10 keystrokes × 10 searches/day / 86400 = ~115K QPS (suggestions)
  • Write QPS: 1B queries/day / 86400 = ~11.5K QPS (query log events)
  • Trie size: top 10M queries × avg 30 chars = ~1.5 GB — fits in memory
  • Cache: prefix cache. Most traffic is top 10K prefixes — fit in Redis (<1 GB)

High-Level Architecture

Architecture

[User types in search box]
        ↓ (every keystroke, debounced 100ms)
[Autocomplete API]
        │
        ├── Redis Cache ──→ HIT: return cached top-5
        │
        └── MISS: query Trie Service
                    │
              [Trie Service] → in-memory Trie
                    │
             top-5 → cache in Redis (TTL: 1 hour)
                    │
               return to client

[Background]
[Query Logger] → Kafka → [Frequency Aggregator]
                                    │
                            hourly batch → update Trie weights

Why a Trie beats naive substring search at scale

A naive approach — scanning every stored query string to check if it starts with the user's typed prefix — is O(n * m) per keystroke (n = number of stored strings, m = average string length), and gets slower as the dataset grows, with no way to bound latency as query volume scales. A Trie makes prefix lookup O(m) — proportional only to the LENGTH of the typed prefix, completely independent of how many total strings are stored — because each character typed simply walks one level deeper into the tree structure, arriving at the relevant subtree directly rather than scanning anything. This is exactly why sub-100ms autocomplete latency at scale is achievable with a Trie and effectively impossible with naive substring scanning once the dataset is large.

API Design

API Design

GET /autocomplete?q=java&limit=5&userId=optional

Response:
{
  "suggestions": [
    { "text": "java interview questions", "frequency": 1500000 },
    { "text": "java stream api",          "frequency": 980000 },
    { "text": "java 21 features",         "frequency": 750000 },
    { "text": "java concurrency",         "frequency": 680000 },
    { "text": "java spring boot",         "frequency": 590000 }
  ]
}

Database Design

Trie Data Structure

Root
  └── 'j'
       └── 'a'
            └── 'v'
                 └── 'a'  ← TrieNode { topSuggestions: ["java interview...", "java stream api", ...] }
                      ├── ' '  → 'i' → 'n' → ...
                      └── 's'  → 'c' → ...

Optimisation: store top-K suggestions at each node to avoid traversal on read:

class TrieNode {
    Map<Character, TrieNode> children;
    List<Suggestion> topK; // pre-computed top-5 at this node
}

This makes read O(P) where P = prefix length (not O(subtree size)).

Storage: Serialize trie to disk (protobuf). Load into memory on service start.

Scaling Strategy

Scaling Strategy

Trie updates (write path)

We cannot lock and rebuild the trie on every query. Strategy:

  1. Log raw queries to Kafka
  2. Hourly batch job aggregates query frequencies
  3. Rebuild trie from scratch weekly with full dataset; apply incremental updates hourly
  4. Blue-green trie swap: build new trie in background, swap atomically

Cache strategy

Top-K prefixes by request volume are cached in Redis with a 1-hour TTL. The top 1000 prefixes serve 80% of traffic — these fit in <10 MB of Redis.

Sharding the trie

If trie is too large for one node: partition by first character (26 shards, or by first 2 characters for 676 shards). Route prefix to correct shard.

Trade-offs

  • Pre-computed top-K vs dynamic traversal: pre-computing top-K at each node makes reads O(prefix length) but makes updates O(n) when a query's rank changes. The hourly batch update is the right tradeoff.
  • Global vs personalized: global suggestions are simple; personalized requires user query history lookup on every keystroke (adds ~20ms latency). Use global as base + rerank with user recency signal.
  • Trie vs inverted index: Tries are optimised for prefix matching; inverted indexes (Elasticsearch) support fuzzy search but add latency. Tries win for autocomplete latency requirements.

Bottlenecks

  • Trie rebuild: takes ~5 minutes for 10M queries. Blue-green swap hides this.
  • Cache invalidation: when a query trends (COVID, World Cup), cache becomes stale in minutes. Solution: streaming aggregation with Flink updates the trie incrementally.
  • Hot prefix: 'a' is prefixed by millions of queries. Cache aggressively; pre-warm on deploy.

Failure Scenarios

  • Trie Service down: fall back to cached suggestions only. 95%+ of traffic served from Redis cache anyway.
  • Redis down: fall back to Trie Service directly. Latency increases from <5ms to ~30ms — acceptable.
  • Bad trie data: version trie snapshots. Roll back to previous hourly snapshot on detection.

Previous · Case study

Design a Notification Service

Next · Case study

Design Uber / Ride Sharing