skip to content

A link's payload was prefetched minutes before the user clicked it — what can go wrong, and how do you bound it?

level: seniorimportance: nice to knowfreq 40%

answer

  1. answered early, shown later
  2. staleness measured from fetch, not click
  3. context can change in between
  4. cap the window or revalidate on click
  5. code does not age, data does

basics

~20 s

A speculative payload ages between fetch and click, so it can render data minutes old or computed under a context the user has left. Bound it with a short hold window, a revalidation on click, or code-only prefetching.

solid answer

~40 s

Prefetching moves the fetch earlier, which means the answer is computed against a world that may no longer exist when the user clicks. The visible failure is a route that renders instantly and wrong — a list without the row just created, a balance from before the transfer. A subtler one is a payload fetched while the user was signed in as one identity, or before a permission changed, and applied afterwards. The usual bounds are a short hold window after which the speculation is discarded, revalidating on the click and showing the prefetched copy only while the fresh answer is in flight, or prefetching only the route's code and leaving data for the click. Which you pick is a per-surface call: a marketing page tolerates minutes, an account balance tolerates none.

go deeper

for a junior

Remember that a prefetched page shows the answer from when it was fetched, not from when you clicked it, so it can be out of date.

for a middle

Explain the window between speculative fetch and click, and the three bounds: a short hold, revalidating on click, or warming code without data.

for a senior

Show you separate volatility from personalisation when deciding per surface, and that you clear speculative state on a context change such as sign-out or tenant switch.

for a principal

Weigh the cost of being wrong against the milliseconds bought, and set a policy that makes the safe choice the default for money, permissions and live figures.

Prefetching buys speed by doing the work early. The hidden term in that trade is **time**: the payload is an answer computed at fetch time and displayed at click time, and everything that changed in between is invisible to it. On a nav bar hovered for 300 ms this is irrelevant. On a link prefetched when it scrolled into view and clicked four minutes later, it is a correctness problem. ## The three ways a speculative payload goes wrong 1. **Stale data.** The world moved. The user created a record, completed a payment, or dismissed something, and the prefetched route still shows the state from before. It renders instantly, which makes it more convincing and more confusing than a slow but correct page. 2. **Wrong context.** The payload was computed for a state the user has left: a different identity after a session change, a different workspace or locale after a switch, a different set of permissions after a role change. Applying it shows content that the current context should not produce at all — occasionally content the current user should not see, if the payload was cached anywhere shared. 3. **A side effect that already happened.** The speculative request reaches the server as a normal request. If the target is not side-effect free, the effect occurred when the link was hovered or rendered, with no click behind it. ## Bounding the window | Bound | What it gives you | What it costs | |---|---|---| | Short hold window, then discard | staleness capped by wall-clock time | re-fetch on click if the user was slow | | Revalidate on click, show prefetched meanwhile | instant paint, converging to fresh | one request per navigation anyway | | Prefetch code only | latency win with zero staleness | the data round trip remains | | Do not prefetch this surface | always correct | full click latency | The first two are the common defaults; the third is the safest thing to do for anything personalised, because a route's code chunk does not age. The fourth is a legitimate choice: some surfaces should not be speculated on. Whether a speculative payload is discarded on a session change is the detail teams miss. Signing out, switching tenant or changing role should invalidate anything fetched before it — otherwise a prefetch made under the old context is sitting there ready to be applied under the new one. ## What to check per surface - **Volatility.** How fast does this route's answer change? Anything derived from a live figure — inventory, balances, queues, unread counts — tolerates almost no window. - **Personalisation.** Is the answer a function of who is asking? If so, warm code and leave data alone, and make sure nothing between the client and the origin caches it. - **Cost of being wrong.** Showing a stale article is a shrug. Showing a stale amount of money is an incident. - **Safety.** Is the target idempotent? If the request does anything, it must not be prefetched at all. ## Diagnosing it in the wild The symptom is a report that a page was correct after a refresh but wrong when reached by clicking, and only sometimes. It reproduces poorly because it depends on how long the link sat prefetched. Useful moves: check whether the affected navigations were served from a speculative fetch at all, look at the age of the payload at the moment it was applied, and try the same route with prefetching disabled — if the bug vanishes, the window is the cause. A timestamp carried in the payload, compared against the moment of application, turns a vague report into a measurement. ## What this is not This is about the window between a speculative fetch and the click. Once a route has actually been visited, the question of how long the client keeps that payload and what invalidates it is a separate caching concern with its own rules — prefetching just gets there first and with less certainty that the user ever wanted it. ## Where frameworks differ Some discard speculative payloads after a short fixed window, some hold them until the page unloads, some treat them exactly like a visited route's copy, and some revalidate on click as a matter of course. Because the default varies, the honest answer in an interview is to state the risk, name the bounds, and say that you would check what your framework actually does before assuming one of them.

  • Why is prefetching only the route's code a safe default for personalised pages?
    Because a code chunk is the same for every user and does not go stale — warming it removes the download and parse from the click path with no correctness risk and nothing per-user cached anywhere. The data round trip still happens on the click, which is exactly what you want for an answer that depends on who is asking and on what changed a moment ago.
  • What should happen to prefetched payloads when the user signs out or switches workspace?
    They should be discarded. Anything fetched under the previous context was computed for an identity or scope that no longer applies, and applying it afterwards can show content the current context would never produce. Treat a context switch as an event that clears speculative state, the same way it clears other per-session state.
  • How would you confirm that a reported wrong-data bug comes from prefetching?
    Ask whether it reproduces on a direct load or refresh — staleness from speculation appears only when the route is reached by a click. Then disable prefetching for that surface and retest. Carrying a generation timestamp in the payload and comparing it with the moment it is applied turns the intuition into a measured age.

A prefetched payload is a photograph taken before the click. It is instant to show, and it keeps showing the moment it was taken, however long it waited.

saying these in an interview costs you the question

  • Assumes a prefetched payload is as fresh as one fetched on the click.
  • Measures staleness from the click rather than from the speculative fetch.
  • Keeps speculative payloads across a sign-out or workspace switch.
  • Prefetches a personalised route's data and lets a shared cache store it.
  • Blames the server for stale data without checking whether the navigation was warmed.
  • Says prefetching cannot affect correctness because it only fetches early.