Reinventing the Wheel: Cache-Busting via Automated File Versioning

I have a tendency to like to reinvent the wheel in my development, just to prove that I can.

I’ve gotten better about it over the years.  I replaced my custom-built blog engine with WordPress on DetroitHockey.Net, for example.  I still roll my own classes for interacting with the Trello and Twitter APIs, though.

Lately I’ve been looking to solve asset caching issues.  If I make a change to main.js, for example, how do I make sure my users get the updated file?  This is something that I’m sure has been solved, but I wanted to give it a shot myself.

We can’t force a user to know that a file has changed and re-download it.  We can tell them how long to cache a file for, and the browser may or may not respect that, but we can’t interrupt that with a new file.

What we can do is version the file.  That’s not a huge deal.  Drop a suffix onto the file name (main.js becomes main-v2.js).  The problem is I’m lazy.  I don’t want to have to rename the file and update all of the places it’s used.  I want that to happen automatically if I happened to change the file.

Conveniently, my build process already had a point where it acted only if a CSS or Javascript file had changed.

I use BitBucket for my source control and BitBucket Pipelines for publishing.  There’s a step in my publish plan that sends a curl request to a listener on my server.  The listener takes a look at each CSS and JS file and minifies it, but it only saves the newly-minified file if it’s different from what’s already on the server.

So I’ve got a point where I know if the file has changed, what do I do with it?

I added a configuration file writable by that listener.  It’s an array stored as JSON tracking a version number for each asset file.  I then updated the references to those files in my site templates to make sure to use the version number from the configuration file is used.

The last step was to make the listener actually write to the configuration file.  The listener, as I said, checks to see if the newly-minified version of main.js (for example) is different from the existing minified version of main.js.  If it is, the existing file is deleted, the configuration file is updated with a new version number based on the time, and the new file is saved with that number.

I don’t have to rename anything.  I don’t have to change any links.  I don’t have to ask anyone to hard refresh.  So far, it works out pretty well.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.