Results briefly jump back to an older query
JavaScript incident
Latest query loses to stale search results
Cached results and slower network results both update the same list, so an older query can briefly show up again.
Failure signals
The clues you start with
Cached and network results both update the same results list
Debounce reduced traffic, but the stale flash is still there
Overview
What failed in production?
Start by making the bug legible: what the user saw, why it mattered, and what the product was doing underneath.
Symptom
A user types 'react', quickly deletes back to 're', and briefly sees old 'react' results again.
User impact
Search feels unreliable because the interface does not reflect the most recent intent.
Environment
Plain JavaScript search with a 250ms debounce, a small cache of recent queries, and one shared results list used by both cached and network results.
Evidence pack
What you can inspect before answering
Request order
The request for 'react' finishes after the request for 're', and both update the same results list.
Current cache + response handling
const cached = cache.get(query);
if (cached) {
resultsStore.items = cached.items;
renderResults(resultsStore.items);
}
const res = await fetch(`/api/search?q=${query}`);
const data = await res.json();
resultsStore.items = data.items;
renderResults(resultsStore.items);Observed behavior
A 250ms debounce reduced the number of requests, but the stale flash still appears on a slow network when you backspace quickly.
Stage 1: likely root cause
What is actually going wrong?
Look for why old results are still allowed to replace the current list.
A longer debounce might reduce overlap, but it does not fix a shared update path that still lets old results replace the screen.
Correct. Cached and network data can both update the same list without checking whether the query is still current.
Fastest is not the same as latest. This framing confuses a race primitive with product intent.
Old cache data can contribute, but the deeper bug is that multiple sources can still update the same visible list with no guard.
Stage 2: debug priority order
What would you check first? Put the best three at the top.
You want to prove exactly where old results are still allowed to replace the visible list.
- #1Reproduce on a slow network while logging the query text, whether data came from cache or network, and the request ids.
- #2Check whether cached results and network responses both write into the same shared results list.
- #3Confirm that the data being rendered belongs to an older query when the flash happens.
- #4Rewrite the search endpoint to return smaller payloads first.
- #5Increase debounce to 600ms before you understand the race.
Only the top 3 positions are scored.
Expected: Reproduce on a slow network while logging the query text, whether data came from cache or network, and the request ids.
Expected: Check whether cached results and network responses both write into the same shared results list.
Expected: Confirm that the data being rendered belongs to an older query when the flash happens.
Stage 3: best fix set
Which changes should be part of the first robust fix?
Pick the changes that make every update respect the latest query.
Correct. One guarded update path is safer than multiple blind writers.
Correct. A request id or query version can stop older responses from winning.
Fastest response is explicitly the wrong goal here. Latest intent is what matters.
Correct. This reduces overlap, but the real guarantee still comes from checking before you update the UI.
Debounce can help load, but alone it does not guarantee correct ordering.
Stage 4: regression guard
What is the best way to keep this from coming back?
Pick the check that proves older results can no longer overwrite the latest query.
Useful, but lower latency does not replace correctness guarantees in the client.
That changes product behavior and still does not solve the stale update logic.
Manual checks are helpful, but weak against timing-sensitive regressions.
Correct. That covers the real messy flow here: multiple sources trying to write into one visible list.
You treated it like a traffic problem, not a correctness problem
The real issue was who is allowed to update the visible list, not only how many requests were sent.
Mostly right, but one safety check is still missing
You saw the race, but the debug order or guardrail did not fully lock it down.
You kept the latest query in charge
You proved the stale flash, guarded the shared update path, and tested the exact cache-plus-network race.
Ideal runbook
How a strong answer should flow
- 1Reproduce the bug and log cached and network results together.
- 2Prove that one shared update path accepts old data after a newer query.
- 3Add guards so only the current query can update the list.
- 4Protect the flow with a cache-plus-delayed-response test.
Reflection
Reflection note
If you keep the cache, what one rule must be true before anything is allowed to update the results list?
The easy trap
Once cached and network data both write into the same list, this is no longer just a 'too many requests' problem. It is also a 'who is allowed to update the UI' problem.
Interview lens
Stronger interview framing
- 1Name it as an async race condition.
- 2Point out that both sources write into the same list.
- 3Explain why the latest query must always win.