skip to content

Version Skew Between Releases

A browser still running the previous release talking to the next: a hashed chunk that is gone, a write identifier that moved, a payload whose shape changed. Asked because atomic deploys do not fix it.

on this pageshow

questions

5

What is version skew between releases of a web app, and why does swapping the whole deployment atomically not prevent it?

level: middleimportance: must knowfreq 55%

answer

  1. two halves, two clocks
  2. the old release runs inside open tabs
  3. atomicity is about responses, not browsers
  4. window equals session length, not deploy length
  5. closes at the next full document load

basics

~20 s

Version skew is a browser running one release's client code while the server serves the next: a missing chunk, a moved write identifier, a changed payload shape. Atomic deploys swap only the server; loaded tabs keep the old client.

solid answer

~50 s

A deployed app has two halves on different clocks. The server half — documents, route data endpoints, write handlers, asset files — is replaced by the deploy. The client half is whatever bundle, route manifest and serialised state a browser downloaded when it loaded the page, and it is replaced only when that browser loads a document again. Version skew is the window where the two halves come from different releases. Atomicity is a property of *responses*: no request is served half old and half new. It says nothing about browsers, because the deploy has no channel into an open tab. Meta-frameworks widen the window on purpose — after the first document the client router fetches chunks and route payloads instead of doing full loads, so every navigation is an old client calling a new server. The window is set by session length, not deploy duration.

go deeper

for a junior

Recall the one-line definition: the code in the browser and the code on the server can come from different releases, because a tab keeps running what it downloaded.

for a middle

Explain the mechanics: a client router fetches chunks and route payloads instead of loading documents, so an old manifest, an old write identifier and old field expectations all meet a new server.

for a senior

Show you have operated it: skew errors spike right after a deploy, you tag errors with the client build identifier, and you keep old assets and old handler mappings alive for a drain window.

for a principal

Frame the tradeoff: how long you keep old releases addressable, how often you force clients to refresh, and how much delivery speed you spend on compatible-by-construction changes.

## What version skew is A deployed web app has two halves that update on different clocks. The **server half** is everything the deploy replaces: the documents that get rendered, the endpoints that answer a client-side navigation with route data, the handlers that accept writes, and the built asset files sitting on the origin or a CDN. The **client half** is whatever a particular browser downloaded at the moment it loaded a document: the JavaScript bundle, the route manifest that maps a route to the chunk files it needs, and any serialised state embedded in the HTML. That copy is replaced only when that browser loads a document again — not when you deploy. **Version skew** is the window in which those two halves come from different releases: a client built against release N issuing requests to release N+1. Meta-frameworks make the window wider than a plain multi-page app does, and they do it deliberately. After the first document, a client router intercepts link clicks, fetches the next route's script chunk and its serialised data, and swaps the view in place instead of loading a new document. Every one of those fetches is old client code talking to a new server. The app is *designed* to avoid the very thing — a full document load — that would refresh the client half. ## Why an atomic deploy does not prevent it "Atomic" is a real and valuable property, but it is a statement about responses, not about browsers. It means no single request is served half from the old release and half from the new one, and there is no moment where a freshly rendered document references assets that have not been uploaded yet. What it cannot do: - The old release is not only on the server. A working copy of it is running in every open tab, every backgrounded phone tab that will be restored later, and every page held in the browser's back/forward cache. - The deploy has no channel into those tabs. Nothing can tell a loaded page that it is obsolete unless the page was written to ask. - The width of the window is therefore set by **session and tab lifetime**, not by how long the deploy takes. If tabs commonly stay open for an hour and you ship four times a day, some clients are an hour behind by design, and the ones people leave open over a weekend are much worse. ## What actually breaks | What the old client holds | What the new deployment did | What the user sees | |---|---|---| | A route manifest naming content-hashed chunk files | Emitted different hashes; the old files are gone from the origin | A navigation cannot load its script: a blank view, or an error boundary | | An identifier for a server-side write handler | Recomputed or dropped that identifier | A submit returns not found; typed input is lost | | Expectations about a route payload's fields | Renamed, removed or re-nested fields | Missing or wrong values, or a render that throws | | A serialised snapshot of state from its document | Changed how that state is produced or read | The old snapshot is interpreted against new assumptions | The reads degrade; the **writes are what hurt**, because the user has typed something and the failure arrives after they pressed the button. ## How the window closes Skew ends for a given browser at its next **full document load** — a reload, a hard navigation, or opening the app fresh. Everything else is a repair strategy that ends in one of those: 1. A failed chunk load triggers one reload, guarded so a genuinely broken release cannot produce a reload loop. 2. A write rejected as unknown surfaces a "this page is out of date" prompt rather than a silent failure, so the user reloads with their input still on screen. 3. The client compares the deployment identifier it was built with against one the server reports, and nudges the user when they differ. ## What shortens or survives the window - **Keep the previous build's assets addressable** for at least a realistic session length instead of deleting them on deploy; hashed filenames make old and new files coexist safely. - **Keep the previous release's server side reachable** for a drain period, or keep the old write identifiers mapped, so an old client's requests still land somewhere valid. - **Change payload shapes additively** for at least one window, so an old renderer can ignore what it does not know. - **Instrument it**: report the client's build identifier alongside every error and compare it with the server's. Skew errors cluster in the minutes after a deploy and vanish; without the build identifier they look like random noise. Meta-frameworks differ in how much of this they do for you — some ship deployment pinning and asset retention as part of their hosting story, others leave all of it to whoever operates the deployment — so the honest answer in an interview names the mechanism and then asks which of them the target platform actually provides.

  • Why can a phone tab that was backgrounded for days behave like a brand-new source of skew?
    Restoring a backgrounded tab can resume the document that was already there rather than requesting a new one, so the client half comes back at whatever release it was loaded from. Such a session can be many deploys behind, and its first action after restore is often a navigation or a submit.
  • Does rendering every navigation on the server remove skew?
    Not by itself. If navigation still happens through the client router fetching a rendered fragment or payload, the code parsing and mounting that response is the old client. Skew only ends when the browser performs a full document load and re-downloads the client half.
  • Which is worse for a user, a skewed read or a skewed write, and why does that shape mitigation?
    The write. A skewed read usually degrades: missing fields, a failed view, a reload fixes it with nothing lost. A skewed write fails after the user has typed and submitted, and a silent failure can lose that input. So writes get the explicit detection, the visible message and the preserved form state.

A restaurant renumbers its menu at noon. Guests who wrote down "number 7" before lunch are holding a stale copy; the kitchen swapped every menu at once and still cannot fix that, because the old menu left the building in someone's pocket.

saying these in an interview costs you the question

  • Claims an atomic deploy makes skew impossible
  • Assumes every user reloads soon after a release ships
  • Says content-hashed filenames prevent skew rather than only preventing wrong content
  • Treats it as a browser cache problem solved by cache busting
  • Thinks the window equals deploy duration instead of session lifetime
  • Ignores backgrounded and restored tabs as a source of very old clients
open as a page

After a release changes the shape of a route's data payload, why can a tab loaded from the previous build break on its next navigation?

level: middleimportance: should knowfreq 44%

basics

~20 s

A navigation fetches the route's data but renders it with code from the release that tab loaded. If the new release renamed, removed or re-nested fields, the old renderer reads what is no longer there and blanks or throws.

open as a page

A long-open tab's form submissions start failing with not-found right after a deploy, and a reload fixes them. What happened?

level: seniorimportance: should knowfreq 46%

basics

~20 s

The client posts a write to a handler address the build minted. The new release recomputed that address and no longer answers the old one, so a stale tab posts where nothing is mapped. Reloading downloads current addresses.

open as a page

As lead of an app that deploys several times a day, how would you decide what one release may change while clients from the previous release are still running?

level: principalimportance: should knowfreq 30%

basics

~20 s

Measure how long sessions really live and make that the compatibility window: inside it, changes to what a loaded client touches must be additive or retained. Add a cheap escape hatch so an out-of-date client reloads without losing input.

open as a page

How can requests from an already-loaded page be pinned to the build that served it, and what does that require of the hosting setup?

level: seniorimportance: nice to knowfreq 36%

basics

~20 s

Stamp a deployment identifier into each document, have the client echo it on every later request, and route on it to the retained older deployment. It needs old versions addressable, a drain window, and an expiry that forces a reload.

open as a page