Build typeahead with stale-response protectionLESSON 10.15 · 15 OF 20 IN CHAPTER
PART C / Data systems at scale
Step 168 of 252
LESSON 10.15 · 15 OF 20 IN CHAPTERTry it, then open the solution

Build typeahead with stale-response protection

Application background

A shopper types a product name into a search box. After each change, the browser asks for suggestions matching the text so far. This is typeahead. A query for iph might return iPhone accessories before the shopper finishes typing.

Network responses can arrive in a different order from the requests. The response for ip may arrive after the response for iph. Displaying whichever arrives last could move the interface backward to an older query.

Example walkthrough

01 · Try this input

Input / starting state
The shopper types ip, then iph
Expected result
Send requests associated with those query versions.

02 · Try this input

Input / starting state
The iph response arrives first
Expected result
Show its suggestions.

03 · Try this input

Input / starting state
The older ip response arrives later
Expected result
Ignore it for the current input.

A popular prefix can also create a large volume of identical lookups. Reusing those results helps server capacity, while browser sequencing protects what the shopper sees.

Your assignment

Deliver: Build prefix suggestions and a browser that displays only results for its current input. Add bounded reuse of common query results to handle busy prefixes.

Required behavior: GET /suggest requires at least three normalized characters and returns five suggestions from a named index version. Target p99 is 150 ms. The browser displays only the response matching its latest query generation.

The required first milestone is a working local implementation of the behavior above. The numbered implementation steps define the scope. The cloud architecture is a later extension, not something the starter has already provisioned.

Get the code and run the supplied example

The code is in the public junior-to-staff repository. Install Git and Python 3.12+. No AWS account or Python packages are required for this first run. If you already have a checkout, use it and skip cloning.

git clone https://github.com/Soulful-Iris/junior-to-staff.git
cd junior-to-staff
python3 examples/architecture-starts/typeahead_search.py

Supplied file: examples/architecture-starts/typeahead_search.py. You can also read or download the source here (download file, source below).

Read the supplied code · typeahead_search.py
read or download the source here · typeahead_search.py
"""Local mechanism demonstration for typeahead-search. No AWS resources are created."""
latest={'generation':2,'query':'iph'}
responses=[{'generation':2,'query':'iph','items':['iphone']},{'generation':1,'query':'ipa','items':['ipad']}]
for response in responses:
    print('display' if response['generation']==latest['generation'] else 'ignore stale',response['items'])
index={'iph':['iphone','iphone case','iphone charger']}
print('Suggestions:',index.get('iph',[])[:5])

This program is a mechanism demonstration: it runs the small scenario in one process and prints the result. It is not an HTTP service, a complete application, or an AWS deployment. A successful run demonstrates this mechanism only. It does not establish the workload or failure guarantees of the application you will build.

Example output from the supplied run:

Generated IDs and timestamps may differ. Compare the state transitions and outcomes.

display ['iphone']
ignore stale ['ipad']
Suggestions: ['iphone', 'iphone case', 'iphone charger']

Set up your implementation workspace

Create work/typeahead-search/ in your checkout (or use a separate repository). Copy the supplied mechanism into that directory as mechanism.py, then extract its state transitions into functions you can call from your implementation. The record and module names below describe what you must implement. They are not a promise that files with those names already exist. Keep a README.md beside your implementation with its exact run commands and observed results.

Local components and state to implement

This table names the records, interfaces or decision inputs for your deliverable. Unless a name is explicitly linked to supplied source above, it is something you create. Implement the local state transitions first, then connect the HTTP, storage or worker boundaries required by the steps.

Record / module Key or interface Responsibility
suggestion_index index_version,normalized_prefix Bounded ranked candidate list.
catalog_source item_id,name,eligibility,version Source for index generation and exclusions.
client_query generation,normalized_text Suppresses stale asynchronous responses.

Implement the assignment

1. Define normalization and eligibility

Specify Unicode handling, case folding, whitespace and locale. Enforce the three-character minimum at both client and server. Exclude unavailable or restricted terms during index construction and define an emergency removal path.

2. Build an immutable index

Generate prefix-to-top-five lists or a compact trie/FST from catalog and ranking data. Write a complete version, validate its metadata and switch a pointer atomically. Keep the previous version available while instances load the new one.

3. Make the browser race-safe

Debounce input, cancel requests where possible and attach an increasing generation. Cancellation is an optimization. The response handler still verifies generation and query before rendering. Preserve keyboard navigation and accessible option announcements.

4. Serve hot prefixes predictably

Cache by normalized prefix, locale and index version. Bound query time and return an empty or recent eligible result under the documented fallback. Record server latency separately from browser debounce and network delay.

Demonstrate the completed local result

01 · Try this input

Input / starting state
Run the starting program
Expected result
The iph response displays. The older ipa response is ignored.

02 · Try this input

Input / starting state
Request a two-character prefix
Expected result
The API returns the documented empty/validation result.

03 · Try this input

Input / starting state
Fail a new index download
Expected result
The prior complete generation continues serving.

Handoff: In your implementation README, include the start command, one successful operation, the failure case above and the resulting stored state or decision. State which dependencies are simulated. Someone with a fresh checkout should be able to reproduce this without your chat history.

Workload assumptions and capacity decisions

These are constructed exercise assumptions. The stated workload is a design target. The local demonstration does not establish that throughput. Use the estimation constants to check units before choosing capacity.

Input or objective Calculation / consequence
80,000 queries/s peak Cache hot normalized prefixes and measure the largest prefix, not just average QPS.
Minimum three characters Use iph as a hot-prefix example. Requests for a are outside this API contract.
Five results/query Precomputed bounded suggestion lists avoid scanning the entire catalog on each keystroke.

Map the local implementation to AWS

Deployment status: local only. Running the supplied command creates no AWS resources and configures no cloud connections. The diagram is a proposed deployment of the completed application. Each box needs either a deployed runtime, a provisioned service or an explicitly external dependency.

Read the diagram by following the arrows from the entry point: application code accepts the request or event, the state owner commits it, and any worker produces the later result. The table ties those roles to code and adapter work. Multiple boxes do not imply multiple Python files already exist.

Build typeahead with stale-response protection: AWS services, their general roles, and the primary data flow

An immutable precomputed index makes the query path small and predictable. OpenSearch completion suggesters are an alternative when the catalog/query requirements fit. Compare operational cost and rebuild behavior.

Local responsibility Cloud destination and role Implementation still required
Local static/media delivery path Amazon CloudFront: static search UI delivery Configure an origin, cache policy and private-content access. Distinguish cached bytes from current authorization.
Local HTTP boundary or the endpoint you will add Amazon API Gateway: suggestion API Create routes and an integration. Translate requests and responses and configure identity validation.
Python operation or worker function AWS Lambda: suggestion handler Write a Lambda event adapter, package its dependencies and give its role only the required resource actions.
Local cache, counter or coordination state Amazon ElastiCache: hot-prefix cache Implement a Redis/Valkey adapter and atomic operations, expiry and unavailable-cache behavior. Keep the durable authority separate.
Local file, object fixture or exported payload Amazon S3: immutable index artifacts Implement upload/download and metadata adapters, scoped access, object naming, retention and incomplete-upload cleanup.
Application or worker process Amazon ECS: index build workers Build a container and task definition. Supply configuration, task roles and graceful shutdown behavior.

Provision resources, then connect the application

Resource or boundary Initial configuration and reason
Cache keys Include locale and index version. Never share personalized suggestions under a global prefix key.
Index rollout Publish complete artifacts before changing the active pointer. Keep loading failures on the prior version.
API limits Minimum/maximum query length, short deadline and bounded response size. Observe p99 by cache hit/miss.

Use one disposable AWS environment for the cloud exercise. Put the named resources in infra/template.yaml or your existing IaC tool, pass resource IDs through configuration, and scope each runtime role to its own tables, buckets and queues. The diagram is a design to implement. It is not a claim that these resources have been deployed. Record the commands you used to deploy and remove the exercise resources.

For concrete provisioning commands, configuration wiring and cleanup, use the AWS foundation guide. It includes a deployable table/queue/object-storage foundation and explains which application and service adapters you still implement.

A provisioned queue or table does not make the local program use it. Configure resource IDs in the deployed runtime, replace the local adapter, and replay the same successful and failing operation against that runtime. Record the deployed commit and observable result, then remove the disposable resources using your infrastructure tool.

Extend the design after the baseline works

Worked follow-up: Personalize suggestions without sharing private history

Caching the final personalized response under prefix iph can expose one person's history to another. Disabling all caching is unnecessary if public candidates and private ranking are separated.

Starting design Changed requirement
Hot prefixes share a public candidate cache. A user-specific reranker uses private history after public candidate retrieval.

Revised architecture. Follow the changed responsibility and failure path below. This is a design to implement. The supplied local example does not provision these components.

Diagram: Worked follow-up: Personalize suggestions without sharing private history

What to implement. Keep the shared key limited to public vocabulary version, locale and normalized prefix. Bound the candidate list before loading user-scoped features. Rerank within a short budget and fall back to the public order when private features are unavailable. Do not put account IDs into the shared candidate payload. Any personalized response cache must include trusted user identity and an explicit deletion policy.

Walk through the result. Alice often searches iPhone repairs and Bob searches iPhone photography. Both reuse the same public iph candidates but receive independently ranked results. Remove Alice's history and repeat. The public cache survives while her private feature record disappears. Deliver the cache keys and one response pair.

Personalize ranking while preserving a hot-prefix cache. Separate a globally eligible candidate set from a small per-user rerank, and define how private history is isolated.

Additional design cases, alternatives and original source notes

This is a commonly listed system-design interview prompt with a concrete practice contract. Assume 80,000 queries/s, 150 ms end-to-end p99, and a 3-character minimum prefix. Clarify service guarantees and a first version before filling the board with services.

01 · Try this input

Input / starting state
Prefix iph
Expected result
Return top five from a bounded precomputed candidate set.

Input / condition: Millions of possible terms

02 · Try this input

Input / starting state
New trending term
Expected result
Publish a new index version without partial mixed results.

Input / condition: Ranking changes in 2 minutes

03 · Try this input

Input / starting state
One hot prefix
Expected result
Cache safely and avoid recomputing full descendant scans.

Input / condition: iph receives 12% of queries

04 · Try this input

Input / starting state
Unicode query
Expected result
Normalize consistently while preserving display form.

Input / condition: User types accented text

Think from the contract to the boxes

Separate offline/stream ranking from online prefix lookup. Build a versioned prefix index containing top candidates by score. Online reads should be bounded by prefix and small K, not scan every completion. Keep raw popularity signals distinct from personalization. Atomically swap index versions and measure suggestion latency, zero-result rate, and freshness.

First diagram: Draw term events → rank build → immutable prefix index → cache → bounded query. Mark index version on response.

AWS service / general role Why it fits this design Alternative and when it fits better
Amazon Kinesis / query/event stream Collect searches and clicks for ranking updates. SQS for simple asynchronous feedback without event-time windows.
AWS Glue / batch index build Normalize and aggregate historical terms. EMR when custom large-scale compute is needed.
Amazon S3 / versioned index files Hold immutable prefix snapshots for atomic publish. DynamoDB when the complete lookup fits key-value access.
Amazon ElastiCache / prefix cache Protect hot prefix reads with short TTL. CloudFront for public query results where personalization is absent.
Amazon ECS / suggestion API Serve bounded prefix lookups and ranking blend. Lambda for sparse traffic with cold-start allowance.

Service choice follows the contract: the box label gives the generic job, while the table explains the AWS product and a reasonable substitute. Name which component owns durable truth, where retries happen, and the guarantee each managed service does not provide by itself.

Pressure-test the design

Follow-up: Prefix iph maps to a bounded list already ranked offline. Extending to ipho performs a narrower lookup. Show where personalization can add or remove candidates.

A new vocabulary release improves quality but makes p99 worse. Split read latency by cache/index/network and roll back only the index version.

Multiple products want one shared suggestion platform. Govern normalization, source attribution, privacy, and quality evaluation without coupling every team to one ranking model.

Practice artifact: Draw term events → rank build → immutable prefix index → cache → bounded query. Mark index version on response. Then trace every row in the table, draw one failure, and state what the customer observes. Suggested rehearsal: 35 minutes design, 10 minutes to challenge the guarantees.

Evidence and origin: The current community interview-question catalog lists typeahead reports at Databricks, Meta, Expedia and Salesforce. It does not show interview dates. The entry does not show the interview date and is not a verified company rubric. The prompt contract, workload, outcomes, diagrams and solution here are original practice material. Treat company tags as reported sightings, not a prediction of your interview loop.

Interview report listing: Open the community question entry.

Sources and further reading · 1