DEV Community

Cover image for The thing about service workers...
Sebastijan Dumancic for PROTOTYP

Posted on • Updated on • Originally published at


The thing about service workers...

For years, service workers have promised us a feeling of a native app straight in the browser. While specific parts of it are true, such as access to device hardware (gyroscope, orientation sensor, etc.) or background sync, we're still far cry from the native feel in areas such as push notifications, which don't work at all in iOS, and especially offline support. While it's fun to have options and experiment on private, small projects, we can't really consider technologies to be production-ready when they don't support half the devices out there.

With that in mind, service workers also come with a lot of baggage. And we've found that out in a hard way.

Problem with service workers

First, you start developing your app, include service worker because it's a cool new thing to do, you want offline support or definitely want to have all the stuff they bring as an option to use. Months go by, you release v1 of the app and users start rolling in. As it's the first version, things are bound to change, so you implement the first couple of changes. You maybe change some copy around, some links, but soon you get a report that you have a huge bug discovered by one of your users. Maybe something potentially catastrophic for your database, it happens.

Fear not, you patch it up and go about your day. Months go by, and you release v2 of the app, market it like crazy and it reaches some top lists of this and that, and same users that entered your app a couple of months ago, and never again, land on your page and load a completely cached version from the last time they visited, together with any hardcoded values, bugs, funnel changes... essentially a snapshot from the past.

By the time you realize what you have done, you've already saved a snapshot of the app in the arbitrary time to thousands of devices, unable to wipe the cache on their devices, and just sit and wait for them to potentially open your older, less secure version of the app from months ago.

Potential solutions

The way service worker works is when users land on your website for the 2nd time, it loads all assets and files from SW and THEN checks if you have pushed a new SW update in the meantime. If it finds one, it schedules it to be loaded next time you open the website. To make matters worse, normal refresh won't load the new SW content, but you have to close the session (i.e. close all website tabs currently open, or whole browser) and reopen the website to load the new version.

By following this article:

I found out that you can insert an action into this process of discovering the new version of SW. The problem is that you can either notify users that a new version is available and leave it up to them to click Load new version which will reload the website properly, or you can force a refresh when the browser figures out that a new SW version is available and installed, which can be after a couple of seconds and well into the time when the app was already interactive, meaning that user started to do something.

Alt Text

Usually, early in the live of an app, updates could be released several times a day, which means users could get a prompt or hard refresh multiple times a day when they land on a website, which is also not a good option.

Other way around, we leave ourselves open to the ghosts of the past by giving users the option to load a new version of the site or not. This way malicious users could take advantage of the app in its pre-patched state.

The worst thing is, when you decide to change your approach, you still have all the old users cached on your previous decision and they don't get the memo that they should reload as soon as they open the site.

Now, when you think about it, it becomes obvious that this is how native apps work from the start. You have a version that is the latest and a lot of previous versions that people have installed, and are still using. But the web isn't native.

As leob mentioned in the comments (thanks!) some apps force users to update to new versions even in the native environment. Banking apps first come to mind, which just confirms that the issue exists and if security is an important priority for us, that we have to address it in a creative way.


Many years ago, Web solved the issue where users had to install a new version of software on their devices, where we had to incentivize them to update or upgrade, and support legacy versions with both features and bugfixes. We approach web development with a different mindset than native development. We KNOW we can push updates very quickly to 100% percent of our users, and we are used to pushing experimental features, knowing very well that we can patch them up as we go or remove them fairly quickly if they turn out to be bad ideas. Including service workers turn the tables around once again, by introducing fragmentation, legacy support, and fear of the unknown on the internet, which is something that should be avoided at all costs.

Sure, if we create just a couple of versions all of which are perfect, this is a non-issue, but let's be real, those apps don't exist. So how do you deal with these issues if you still want to keep offline support? Do you use service workers at all? Let me know!

Top comments (8)

leob profile image
leob • Edited

Great points, that sounds like an instance of "have your cake but not be able to eat it" ... I don't have real hands-on experience with this kind of stuff, but it sounds like you really have to force your users to upgrade, at least in case of "major" upgrades, I think there are also native apps which do that:

"Sorry, but your app version is incompatible with our services, please click HERE to upgrade ..."

So, on app startup, but also at strategic places after that, I'd do a version check "is this frontend app compatible with the current backend API" and then show a link or button which they have to click to upgrade. Make it mandatory only for "major" upgrades.

sebastijandumancic profile image
Sebastijan Dumancic

That's a good point, I wanted to touch on that but forgot. Updated in the article, thanks :)

Issue is that you HAVE to have event that check 100% done right in the first version that hits the web, otherwise you risk caching users to the version without the check. That's the issue actually - when you release a major upgrade, you are not sure that everyone is seeing this upgraded app when they are opening the app for the first time after some time.

leob profile image

Haha that's absolutely right, that check has to be there and working perfectly in version 1.0 of your app ... I think this should be mentioned in EVERY article or tutorial that touts the benefits of PWAs. And you're right that an "old fashioned" web site (not a 'PWA' or an 'app') doesn't have these issues!

dar5hak profile image
Darshak Parikh

While I don't have much experience in this, I think a conservative approach would work here:

  1. Release v1.0 without any service worker. (Honestly, we haven't reached a time when everyday people complain about the lack of one.)
  2. Observe the rate at which changes come. This is a curve that flattens over time.
  3. Identify the resources which make good candidates to cache. These are things that you can afford to have old versions of, like icons, fonts and non-critical styles.
  4. Write a service worker to cache only these resources and release it. Do display the refresh prompts to users. This stage is meant to test the service worker itself once your core functionality is relatively stable.
  5. Once you know that your service worker is robust enough to handle updates smoothly, add more resources to cache.
  6. Iterate.
sebastijandumancic profile image
Sebastijan Dumancic

While I agree with your process, caching static assets is solved years ago with server-side caching and cache busting works flawlessly for serving new content when it's available without falling back to old ones first. The issue with offline support for SW is that it's all or nothing, and not caching specific assets. That should be handled by the server, not the browser, either way. Let me know if I'm understanding something wrong about SW config :) Thanks for the comment!

dar5hak profile image
Darshak Parikh • Edited

Caching using headers is indeed a breeze, but it falls short in two scenarios:

  • When a user has zero internet connectivity, the site won't load unless every resource is cached. (Not tested, but I'm fairly sure.)
  • You cannot control cache headers when serving from a static host like GitHub Pages or Surge.

Service workers offer a lower-level caching mechanism that can bypass these limitations.

That said, if you can control your headers and don't plan on offline access (but are caching for perf improvement), headers are the way to go.

jwp profile image
John Peters

I don't know how Adobe's PDF viewer, and Google Chrome do this but; it's all silently done in the background unless we turn it off. I feel following this solution is the best way forward.

sebastijandumancic profile image
Sebastijan Dumancic

Any links for the approach you're talking about? Thanks!

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.