Cache Versioning and Cleanup in Service Workers
Cache versioning gives a service worker a way to distinguish current cached assets from obsolete ones and remove stale caches during activation. It is a key part of safely rolling out updated frontend resources without leaving outdated cache entries behind.
A service worker can store responses in named Cache Storage entries, such as `app-v1` or `app-v2`. When the application changes, using a new cache name creates a clear boundary between the resources managed by the new worker and those belonging to an older version.
During the `activate` event, the worker can inspect existing cache names and delete entries that are not part of the current cache set. This cleanup is commonly performed with `caches.keys()` followed by `caches.delete()`.
The `install` event is typically responsible for populating the new cache, while `activate` is the appropriate lifecycle stage for removing obsolete caches. Waiting for cleanup with `event.waitUntil()` keeps the activation work associated with the service-worker lifecycle rather than allowing it to finish prematurely.
Versioning does not by itself guarantee that every browser tab immediately uses the new worker. Existing controlled pages can continue under the previous worker until the normal service-worker lifecycle permits the new worker to take control. Features such as `skipWaiting()` and `clients.claim()` change that behavior and should be used deliberately because they can introduce mixed-version application behavior.
Key points
- Give each deployment or asset set a distinct cache name.
- Precache new resources during `install`.
- Delete obsolete cache names during `activate`.
- Use `event.waitUntil()` for asynchronous installation and cleanup work.
- Cache cleanup and immediate worker takeover are separate concerns.
Interview questions
Why would a service worker use versioned cache names?
A versioned cache name separates resources from different application deployments. When the worker is updated, it can populate a new cache without overwriting the old cache immediately. During activation, the worker can delete caches that no longer belong to the current version. This makes stale-resource cleanup explicit and predictable.
How would you implement cache cleanup in a service worker?
I would identify the cache names that the current worker owns and enumerate the existing Cache Storage names with `caches.keys()`. Any obsolete application cache would be removed with `caches.delete()`. I would wrap the asynchronous operation in `event.waitUntil()` so activation does not complete before cleanup has finished. If multiple caches are being removed, I would typically coordinate the deletions with `Promise.all()`.
What is the difference between updating a cache version and forcing a new service worker to take control?
Changing the cache version controls which stored resources belong to the new worker. It does not by itself force the newly installed worker to activate immediately. `skipWaiting()` can allow an installed worker to become active without waiting for older workers to finish normally, while `clients.claim()` can let an active worker control existing clients. These mechanisms affect lifecycle and control timing rather than cache deletion itself.
What risks arise if old caches are removed incorrectly?
An overly broad cleanup rule can delete resources still required by another worker or application version. That can cause unexpected cache misses, offline failures, or inconsistent behavior across clients. I would therefore use a clear ownership convention for cache names and retain every cache that the active application legitimately needs. I would also test upgrades from several previous versions rather than only testing a clean installation.
How would you design a service-worker cache migration for a large frontend application?
I would define stable ownership and naming conventions for each cache and make the new worker install its required resources before activation. During activation, I would retain only explicitly supported cache generations and remove known obsolete ones. I would separate resource migration from worker takeover so that rollout behavior is understandable and reversible. I would also consider atomic-looking asset sets, content-hashed filenames, failure handling, and compatibility between the worker and pages that may already be open.