Auto-update your App

Keep your app fresh as a lemon

A key component of single-page applications is the need to never refresh. However, this raises a critical question for the application developer: how do you get new versions of your app to your users? In this post, we’ll look at an approach we developed at Pebble, based on Google Inbox, and provide some code you can use as a base to build your own auto-updating tool.

The Problem

As a web developer, you’ll be familiar with all the common advice when it comes to static assets: set long cache timeouts, use hashes as part of the filename and update them with your app. This is all solid advice — and we’ll be applying some of these lessons here too — however it all assumes you’re building something more like a classic web application, where something will cause you to load a new HTML file from the server, getting the latest .js bundle with it.

User flow through a typical web app

But what if your page never reloads? You’re never giving your Django/Rails/Express server a chance to send down the latest bundles with your page to get the latest and greatest experience to your users!

In this flow, existing users miss out on your new app version!

But surely most users will close their browser down or close the tab at some point? You’d be surprised, we’ve seen users running versions of our JS apps that were updated over 2 weeks ago!

Building Our Solution

Hopefully I’ve convinced you of the need to think about this when you’re building your applications. So how do we go about building this?

What Should It Look Like?

First up, we need to decide what our process should look like for the user. Once we know this, we can start building a process around delivering what the user needs, as opposed to trying to adapt the user to our process.

An example alert that we could use

Our starter template here gives an idea of the sort of thing we could use to tell people we have an update available — a simple alert widget.

How Do We Make Updates Available?

The next step is to think about your code deployment process. When building this app, we made a change to how we deliver static assets to our users. Previously, we would have our server compile the assets with Webpack then collect them using Django’s collectstatic command, serving them using the {% static "js/bundle.js" %} template tag.

The major downside to this was that updating our JS required a full deployment of our application, so we decided to uncouple these like so:

  1. We separated the JS into its own repository that was built and tested separately.
  2. We configured Webpack to build production bundles with a version number and hash as part of the filename.
  3. We setup CircleCI to upload all static assets to S3, proxied behind our CloudFront configuration.
  4. We built an app in Django that let us define the version number and bundle hash associated with the number.
  5. This included a template tag that would always output the path of the latest bundle.[hash].[version].js at our CDN.
  6. Add an endpoint that returns the latest version number of our app.
  7. Build a simple polling widget that looks up this endpoint and compares it to the current loaded version.

We could join this up with our notification widget to tell the user a new app version is available and to update.

Webpack Config

Webpack config for production builds

This Webpack config will load all the files into a public directory with bundles. The result of this can simply be uploaded to S3 using awscli:

aws s3 sync public s3://mybucket/public --acl public-read --cache-control max-age=120

Django Output

To get Django to output the latest bundle, you just need to output the version as part of your index.html:

The exact implementation will depend on backend, so I’ll leave that as an exercise for the reader. If it’s possible, we’ll be open sourcing it as a Django application.

View Implementation

The most important part here is in view.js which polls the version endpoint and, on update, shows the notification update.

As an aside, this isn’t the best way — we use the Radio and Application on our own app — but this demonstrates the basics for how you’d want to implement this.

Getting The Update

Now we have all the building blocks in place, what does the Update Now link do? Simple: it runs window.location.reload and that’s it! We simply needed to give Django an opportunity to get the latest JS from the server and a full page refresh is the best way to do this.

Conclusion

I hope you enjoyed this short piece and you’re able to put this code to good use! I’m looking forward to see more auto-updating web apps in future. If you find any great examples, please tweet them to me @scott_walton and I’ll be sure to check them out!

Until next time!