After a new deploy of an Angular app using @angular/service-worker, why do returning users see the old version first?
answer
- cached version served first
- the ngsw.json manifest
- download in the background
- next load or reload
- one version per tab
basics
~20 sThe Angular service worker serves the version it has already cached without waiting for the network, then checks ngsw.json, downloads a changed version in the background, and serves it on the next load or reload.
solid answer
~40 s`@angular/service-worker` treats each build as a **version**, identified by the hashes in the generated `ngsw.json` manifest. For speed it answers a returning visit from the installed version immediately, and during initialization (and on navigation requests) it fetches `ngsw.json` to look for a newer one. If the manifest changed, it downloads and caches the new version in the background, and the next time the page is loaded or reloaded it serves the new version. A tab that is already running keeps its version until reloaded, which guarantees that lazy chunks come from the same build as the shell. `SwUpdate` lets the app notice the ready version and prompt the user to reload instead of waiting for the next visit.
go deeper
Recall that the Angular service worker serves the cached version first and switches to a new build on the next load or reload.
Explain versions, the ngsw.json manifest with file hashes, background download and the per-tab guarantee.
Decide how quickly users must move to a new build and wire SwUpdate prompts or checks accordingly, without breaking running tabs.
Balance instant loads against release freshness for the product, including how long old versions may stay in the field.
## What the Angular service worker caches `@angular/service-worker` is Angular's pre-built service worker. The build generates **`ngsw.json`**, a manifest listing every file the configuration in `ngsw-config.json` covers, with a content hash for each. The worker (`ngsw-worker.js`) caches those files and serves them from its cache, acting like a CDN edge inside the user's browser. The key concept is the **application version**: the set of files from one build. Any change to any covered file changes its hash in `ngsw.json`, and the worker treats the whole set as a new version. Versioning the files as a group matters because they refer to each other: - `index.html` references specific bundle filenames; - the main bundle references lazy chunks whose names include build hashes; - mixing an old `index.html` with a new bundle, or an old bundle with a missing chunk, breaks the app. ## Why the old version appears first On a returning visit the flow is: 1. The browser asks for the page; the service worker answers **from the cached version immediately**. It deliberately does not wait for the network, because speed is the point. 2. While initializing, and on navigation requests, the worker requests **`ngsw.json`** (with a cache-busting query parameter) to check for updates. 3. If the manifest differs, it **downloads and caches the new version in the background**. 4. The **next load or reload** of the app is served from the new version. So the first visit after a deploy shows the previous build, and the one after that shows the new build. Nothing is broken; this is the designed trade-off between instant loading and immediate freshness. ## The per-tab guarantee The Angular worker promises that **a running tab keeps running the same version**. A tab opened after the update gets the newest version, so two tabs can run different builds at once. This is a stronger guarantee than a plain web deployment, where a running app can request a lazy chunk that the server has already replaced. The version of a running tab changes only in a few cases: | Cause | Kind | |---|---| | The page is reloaded or refreshed | Normal | | The app calls `SwUpdate.activateUpdate()` | Normal, explicit | | The current version fails hash validation | Error | | The worker enters safe mode after an error | Error | Old versions are cleaned up once no tab uses them. ## Getting users onto the new version sooner Waiting for the next visit is fine for many apps. When it is not, the app injects **`SwUpdate`**: - `versionUpdates` emits `VERSION_READY` once the new version is downloaded and ready; - the usual response is to **prompt the user and reload** the page; - `checkForUpdate()` asks the worker to check now, for long-lived tabs that rarely reload. ## Common misunderstandings - **"The deploy failed."** The worker is serving the cached version by design; check the server log for the `ngsw.json` request. - **"Users must close every tab."** That is the browser's rule for installing a new worker *script*. The Angular worker's own script rarely changes between builds; your app versions are switched inside the same worker, on reload. - **"Hard refresh fixes it for everyone."** It fixes one browser; the real fix for users is an update prompt or simply their next load. - **Testing with `ng serve` in development.** The service worker is normally enabled only for production builds (`enabled: !isDevMode()` is the usual option), so local testing uses a production build served by a static server. ## Checking what a client is running The Angular worker exposes a debug page at `ngsw/state` under the app's path. It shows: - the **driver state**, `NORMAL` or one of the degraded states; - the **latest manifest hash** the worker knows about; - when the **last update check** ran; - each cached **version** with the **clients** (tabs) using it. Comparing that hash with the `ngsw.json` currently on the server tells you immediately whether a client has seen the new version yet, which is far more reliable than asking a user to describe what is on screen. The browser's own developer tools also show that requests are served from the service worker rather than the network. ## Interview summary Say that the worker serves the installed version first, checks `ngsw.json`, downloads the new version in the background and switches on the next load, and that each running tab keeps its version so the shell and lazy chunks always match.
- How does the Angular service worker decide that a new version exists?It fetches `ngsw.json`, the manifest the build generates from `ngsw-config.json`, which contains a hash for every covered file. If the manifest differs from the installed one, any changed hash, the worker treats the whole file set as a new version and downloads it.
- Why can two open tabs of the same Angular app run different builds?The worker guarantees that a running tab keeps its version, so its lazy chunks always match its shell. A tab opened after an update is served the newest version. Old versions are cleaned up when no tab uses them any more.
It is like a newsstand that hands you today's cached paper instantly while quietly ordering tomorrow's edition, which you only get on your next visit.
saying these in an interview costs you the question
- Users must close every tab before the new Angular version is served
- The service worker checks for updates before serving the page
- A running tab silently switches to the new build mid-session
- Seeing the old version after deploy means the deployment failed
- Each changed file is updated individually, not as a version