The key idea
Leave the interviewer with a clear user flow, explicit state ownership, a usable API contract, and an explanation of what happens when the happy path breaks.
A framework you can adapt
RADIO is a mnemonic created by Yangshun Tay at GreatFrontEnd: Requirements, Architecture, Data model, Interface definition, and Optimizations. It helps you cover the major parts of a system design discussion. Use it as a checklist; revisit decisions when requirements change and follow the interviewer's priorities.
- 1
R · Requirements
Agree on the user experience and constraints.
- 2
A · Architecture
Assign responsibilities and trace a user action.
- 3
D · Data
Name the state and decide who owns it.
- 4
I · Interfaces
Define inputs, outputs, and failure contracts.
- 5
O · Optimizations
Address the risks that matter for this product.
One worked example
The rest of this guide designs a hypothetical product-search autocomplete. The numbers and endpoint are example decisions to discuss, not an implemented API or a universal template.
R · Make the requirements testable
Start with a proposed scope: a shopper types a product name, sees suggestions, and chooses one to open its detail page. Ask whether the widget searches a small local collection or a large changing catalog. That answer determines whether filtering belongs in the browser or behind an API.
- Confirm selection behavior: choosing a suggestion navigates to a product; submitting an unmatched query opens a search-results page.
- Agree on limits: start suggesting after two characters and show at most eight items. Explain how these example limits balance discovery, payload size, and mobile space.
- Clarify language, keyboard, touch, and input-method support. Someone composing Japanese text must not trigger a search for every unfinished composition.
- Define the fallback: typing and submitting a query still work if the suggestions service fails.
Turn adjectives into observable behavior
Replace 'fast and accessible' with 'typing remains responsive on a mid-range phone; users can select or dismiss suggestions with a keyboard; slow results cannot overwrite a newer query.' Ask which conditions the interviewer wants you to prioritize.
A · Trace the action through the system
Keep the widget's interaction controller separate from the search transport. The controller decides what the user should see. The transport handles requests and caching. The parent page owns navigation and the selected product. This boundary lets the same widget appear in a header, a mobile search screen, or an admin tool without duplicating request logic.
- 1
Input
Update visible text immediately; wait for composition to finish.
- 2
Controller
Clear an obsolete active suggestion and schedule the query.
- 3
Search adapter
Read a scoped cache or request suggestions from the API.
- 4
Results
Apply only the current response, then render the listbox.
- 5
Selection
Send the product to the parent, which handles navigation.
Use ordinary request-response HTTP for this example. The catalog does not need a persistent connection merely because users type frequently. If live inventory must appear in each suggestion, discuss freshness separately and let the server remain authoritative about stock.
D · Give each piece of state an owner
| State | Owner | Important rule |
|---|---|---|
| Product: id, label, destination | Search service; copied into the response | Use a stable ID, not the array position, for selection. |
| Query, open, activeId | Widget controller | Changing results must clear an active ID that no longer exists. |
| Status: idle, loading, success, error | Request controller | An empty successful result differs from a failed request. |
| Items, request generation | Request controller | Only the latest generation may update displayed results. |
| Cached response and expiry | Search adapter | Key by query, locale, and any user or tenant scope. |
| Selected product | Parent page | The widget emits selection; the parent decides its effect. |
Avoid storing values you can cheaply derive, such as an active index alongside an active ID. Make account changes invalidate private cached data. Decide whether recent searches should survive reloads; storing them persistently creates a privacy and deletion requirement that an in-memory cache avoids.
I · Write the contract at both boundaries
Example search contract
httpGET /api/search/suggestions?q=chair&locale=en&limit=8
200 OK
{
"query": "chair",
"items": [
{ "id": "p_42", "label": "Desk chair", "href": "/products/p_42" }
]
}
429 Too Many Requests
{ "code": "RATE_LIMITED", "message": "Please try again shortly." }Specify maximum query length, supported locales, response bounds, and error shapes. Fetch does not reject simply because the server returns an HTTP error; the adapter must inspect the status, validate the response shape, and translate failures into a safe display message. Keep operational details out of the user-facing error.
Example reusable widget boundary
typescripttype Suggestion = { id: string; label: string; href: string };
type SearchProps = {
label: string;
search: (query: string, signal: AbortSignal) => Promise<Suggestion[]>;
onSelect: (item: Suggestion) => void;
onSubmit: (query: string) => void;
};O · Solve the request race before tuning speed
Imagine 'ca' returns after 'car'. Rendering responses in completion order shows the wrong suggestions. Increment a request generation whenever the effective query changes; apply results only when their generation is still current. Also cancel obsolete fetches with AbortController to reduce wasted work. Cancellation supports efficiency; the generation check provides the explicit correctness rule.
Debounce the request
Try a short delay such as 150–250 ms, then measure. Update the input immediately. Clear timers when the query changes or the widget unmounts.
Bound the cache
Choose an entry limit and expiry. Reusing a recent query saves a round trip, but unlimited query keys accumulate memory and may retain private searches.
Keep results honest
If previous results stay visible while loading, mark that state clearly and prevent selection of an item that belongs to a different query.
Retry deliberately
Aborted searches are expected, not errors to announce. Avoid retrying an obsolete query; respect rate limits and offer a deliberate retry for a current failure.
Make the interaction safe and usable
- Follow the WAI-ARIA editable combobox pattern: label the input, expose expanded state and its popup relationship, and render suggestions as listbox options. Keep DOM focus in the input; aria-activedescendant identifies the active option.
- Support arrow-key exploration, Enter selection, and Escape dismissal. Preserve native text editing shortcuts and the user's typed query. Announce result counts or errors without interrupting every keystroke.
- Test narrow viewports, zoom, touch targets, and long translated labels. The popup must remain visible around the on-screen keyboard.
- Treat query text and suggestion labels as untrusted text. Avoid building highlighted matches with raw innerHTML; use text nodes or framework escaping. Validate navigation destinations against allowed internal routes.
- Enforce search authorization, query limits, and abuse controls on the server. Choose whether query strings may enter logs or analytics, and redact sensitive searches. A hidden UI element does not authorize access.
Validate the design and finish with trade-offs
Measure typing-to-paint responsiveness separately from query-to-suggestions latency. INP describes interaction responsiveness; it is not a measure of the entire network search. The published good INP threshold is at most 200 ms at the 75th percentile of field page loads. Use representative devices and network conditions, then inspect request counts, latency, failure rate, and stale responses discarded.
- Unit-test query normalization, cache expiry, and the state transition rules. Control timers and resolve older promises after newer ones to prove stale results cannot win.
- Integration-test keyboard selection, input composition, clearing a query during a request, zero results, malformed responses, timeouts, rate limits, and unmount cleanup.
- Run a real-browser flow on mobile and test with a screen reader. Automated accessibility checks cannot establish that focus and announcements feel correct.
An optional 40-minute budget
Try 5 minutes on requirements, 8 on architecture, 5 on state, 7 on contracts, 12 on the highest-risk deep dives, and 3 to recap. This is a rehearsal suggestion. Adjust to the interviewer's time limit and questions; a focused component may need much more interaction detail.
Close by stating your trade-offs: server search keeps the catalog fresh but introduces network failures; debouncing reduces requests but adds a small wait; caching helps repeat queries but needs bounds and scoping. Name the evidence that would change a decision and the next risk you would investigate.
Sources & further reading
Original explanations and examples by FineFrontend, informed by the guide and primary references below.
- GreatFrontEnd · The RADIO Framework (opens in a new tab)
RADIO framework created by Yangshun Tay. The autocomplete walkthrough and example decisions on this page are original.
- W3C WAI-ARIA · Combobox pattern (opens in a new tab)
Keyboard behavior, focus management, and accessible combobox semantics.
- MDN · AbortController (opens in a new tab)
Canceling fetch requests and other abortable asynchronous operations.
- MDN · Using the Fetch API (opens in a new tab)
Response status handling and request cancellation.
- web.dev · Interaction to Next Paint (opens in a new tab)
Interaction responsiveness and the published INP thresholds.
- MDN · Cross-site scripting (opens in a new tab)
Keeping untrusted input from becoming executable page content.