Dealing with releases of single page applications
You’ve just released the new version of your single page application, but your users won’t refresh their browsers until.. when?
This is a common problem for single page applications. Particularly for us because we build our admin interface internally and push releases at least weekly, sometimes with hotpatches in between. With every release there’s a risk of the application breaking if you try to run the old version against the new backend, and even if nothing fatally crashes, we always want our employees to get the newest features and bugfixes as soon as they are available. We have time and again encountered situations where people are running an old version, and undefined behaviour occurs.
Spotify does this. They run their application as an HTML app in a boxed browser, and each time they push a release you will get a notification in the application. So why shouldn’t we be able to do it?
There should be a simple solution to this! When you make a release of a single page application, you’ll run your build step to compile all files into the bundle that will be your whole application. This means that all files will be written anew, so if we can just find out how old the entry file (usually /index.html) we’re running is and how old the current entry file on the server is, we will also be able to deduce if there’s a new version available or not and take action based upon that. In our case, we want to display an alert telling the user to refresh their browsers.
Since we have the almighty power and privilege of deciding which browsers our employees are going to use internally, we also have the latest and greatest features at our disposal (if this is not your case.. Just keep calm and carry on reading). HTML5 is handy enough to give us the document.lastModified property, so just looking at that we will know when the current version was released. Now, how do we check if there’s a new version of the application on the server? HTTP If-Modified-Since! By specifying the If-Modified-Since header when requesting our resource we will either get a 200 if the resource has been updated, or a 304 if not. Long story short: if we provide the If-Modified-Since header with document.lastModified we will get a response code 200 if there’s a new version. Cookies for everyone!
Please don’t do this.
This magnificent solution slipped through our testing process, and had a blast playing around in our live production environment (Specifically, I had a slightly irritated Chief of sales hanging over my shoulder a short while after the release).
As it seems way too often, there’s some browser inconsistencies bubbling up here (life of the frontend dev). Embarrassingly enough, if we had just paid more attention to the docs we would of course have seen this coming.
Well. Ok. Enter round 2!
This solution might be better anyway, since it doesn’t rely on document.lastModified, and hence can be used with way older browsers. Basically any browser, as long as everything complies with HTTP/1.1
We need the age of the current running version, otherwise we’re not going to get anywhere. We could extend our build step to include last build time into the js-bundle somewhere, but that requires us to add stuff to the build step. We would like to avoid that if possible to keep the build step simpler and cleaner.
There is however still the Last-Modified response header. If we instantly at startup send a HTTP HEAD request, we can store the Last-Modified header value instead. This also has the added benefit that the Last-Modified header and the If-Modified-Since headers both uses the HTTP-Date format, so we won’t need to consider date formatting any longer.
Finally, we have everything we need. Get the current document age with a HEAD request and then poll the server with HEAD + If-Modified-Since headers, and we will know when a new version has been released.
This has been running in our live production system for a couple of months now, and no more do people come by the tech department just to leave with a refreshed browser tab. And let’s face it, we would much rather do coding than refreshing our users’ browsers.
Bonus: If you don’t use livereload or similar tools while developing, or the browser wasn’t reloaded for whatever reason, you will also get the added benefit of seeing when the build is finished in the browser while developing!
Did you find this useful? Do you do the same thing in a different way? Leave a comment below!