You might not need a service worker

Fudgel2 pts0 comments

You might not need… a service worker

I’m Jay, a product engineer based in East London. A polyglot at heart,<br>I'm a Rust lover, a Go skeptic and a TypeScript apologist. Right now I’m<br>helping shape the future of procurement software at Omnea.

Neciu recently broke down some interesting use cases for service workers. I definitely felt ‘seen’ by this:

The two people in my survey who “tried one in 2019 and removed it” both told the same story with different details: a service worker with a bad cache strategy served a stale app to users, and the fix required shipping a killswitch worker and waiting days for clients to pick it up, because the broken worker controlled when updates were checked.

Back when service workers launched, I was an early adopter, and quickly foot-gunned myself in a similar scenario.

Let’s break down a few examples from the post (if you haven’t read it, please do so first!).

Use cases in the wild

Slack’s instant boot

The most compelling example in the post is Slack’s: caching the full asset set and rehydrating Redux state so that the UI can render before a single network request resolves.

The asset bit feels a touch overblown, though:

They observed that almost nothing in that asset set changes between boots.

The user who opens Slack on Tuesday morning downloads the same JavaScript they downloaded Monday morning.

An HTTP cache should be enough to alleviate this, and is far simpler. For unchanged assets, content hashing plus Cache-Control: public, max-age=31536000, immutable means they should get served directly from the cache.

What it won’t do is provide a network-free boot: you’d still need to fetch the HTML and any prerequisite data. I’d argue that this is more of a question of ‘do I need offline support?’ For Slack, sure, but for many apps, probably not.

If all you’re looking to do is avoid repeated downloads of the same assets, just hash them and leverage the native caching.

Keeping dead chunks alive across deploys

This is an interesting one. Some vendors, like Vercel, have ‘skew protection’, but most of us have run into this in the past: an old bundle on the client results in a 404 when the referenced asset no longer exists.

The more you ship, the more frequently this becomes a problem (if you practice true CI, you might be shipping hundreds of times a day).

Neciu’s solution here is to use a service worker to cache the app locally. However, this implies caching everything in the background:

"version": "2026.06.04-1412",<br>// Where does this end?<br>"assets": ["/assets/index-c91d44.js", "/assets/Settings-c91d44.js"]<br>}jsonc<br>In my mind, this defeats the point of route/code splitting. Sure, you get a faster initial render, but it means every invalidation forces the client to refetch the entire app. For most apps I’ve worked on, this would result in a huge, mostly wasted payload. We can’t predict what components/pages the user will visit with any certainty, so in theory we’d need to pull down the contents of the entire manifest.

What if, instead, we just kept static assets around (for a grace period)? Instead of outright deleting them, let them live on in a bucket. With content-hashed filenames, a deploy never overwrites anything: Settings-a3f8b2.js and Settings-c91d44.js can coexist.

Skew protection is actually just an even more extreme version of exactly this. In a truly serverless architecture, the cost of keeping around entire versions of the app is negligible.

Since the service worker doesn’t run indefinitely in the background, the core refetching logic has to live in the main app anyway:

The page drives the polling instead, posting CHECK_VERSION on an interval and on visibilitychange, so a tab that comes back from a weekend in the background checks immediately.

So this doesn’t need a service worker, either.

Mux’s manifest rewriting

This is neat, but feels like it shouldn’t live on the client. The bug mentioned in the article is actually a symptom of the logic living client-side:

A video player starts fetching the moment it mounts, before a same-page worker can take control, so they had to register the worker on an index page and link onward to the player page.

Instead, move the rewrite server-side, where it’s more robust and easier to test. Only the manifest (a text file) needs rewriting, so there’s no concern about pulling a massive video through additional layers of infrastructure.

It’s even called out in the article:

…because edge runtimes like Cloudflare Workers implement the same fetch event API, they deployed the stitching worker to Cloudflare unchanged and got a working URL.

Partytown

A good example, although worth noting that the service-worker version is actually the fallback:

Partytown will use Atomics and SharedArrayBuffer when they’re available by the browser.

SharedArrayBuffer unfortunately only works under cross-origin isolation, and those headers tend to break third-party embeds. So in practice the service worker fallback gets used more than...

worker service assets cache slack asset

Related Articles