Disclosure: I may earn affiliate revenue or commissions if you purchase products from links on my website. The prospect of compensation does not influence what I write about or how my posts are structured. The vast majority of articles on my website do not contain any affiliate links.

A long time ago, I took over a web app that was one of the most critical parts of our technological risk management platform–the trade monitoring UI. I made changes on a rather dilated release cycle, usually pushing non-critical enhancements into prod after thorough vetting in a simulation environment and, as I often joked, after a certain amount of prodding.

Using this web app, a user with sufficient privileges could start, stop, and, most importantly, kill trades. This was the first (and, by far, the most effective) line of defense against an application gone awry.

One day, the time came to release a thorough refactoring of the code. At that point, I was of the opinion that refactoring often isn’t worth the time or risk, but I was determined to give it a shot anyway. I released and slowly tested most of the features. Everything was going well. I finished my testing, gave the virtual thumbs up, and began my daily turndown before leaving the office. Then I received a message: “Hey Tim, nothing happens when I kill a trade.”

Weird. I had tested the kill function myself. Just to be sure, I tested it again. It worked. I ran over to my colleague’s desk, brought up the Chrome console, and tried again. There was an error being thrown by the server-side Javascript. Though I tell people that my Javascript days are behind me, it’s similar to when I used to tell people that my running days were behind me when I was still thinking about running on a daily basis. It’s more about shedding an identity than spurning an idea. Javascript was a minor, though integral, part of the application and my past experience helped me quickly realize that this behavior was due the previous version of the Javascript code being cached and throwing an error when the updated Python code was interacting with it.

Immediately, I sent out instructions on how to perform a hard reload with cache clearing in Chrome (you open the developer tools console and right-click the refresh button to see the option). As I paced around the office, it was clear that a crisis had been averted, but I was left wondering what I could do about the pesky cached Javascript next time around. Sending out an urgent message is not acceptable, and, in larger (or consumer-facing) organizations, would not be feasible.

I needed to cache bust.

To help build intuition for what gets cached, go to your favorite website and open the Chrome developer tools console. Then, navigate to the network tab and refresh the page. This will show the full query URLs with information differentiating those that were loaded from cache from those that were loaded dynamically. It’s a feature I’ve been using on a more regular basis, as one of the WordPress pages that I manage for a side business was at one time perhaps the slowest site on the internet.

Generally, you want to cache. Caching is good; slow site loading is bad. If you disagree with me, though, the first thing you can do is implement one of the methods discussed in this StackOverflow question. It’s a heavy-handed method but it should work.

What if you decide that you want to cache but you want certain Javascript resources to be loaded each time the page loads no matter what? My wisdom is that, generally, there is no use case for this. If you are set on doing it, though, you can generate a random number or high-resolution timestamp to ensure the cache will be busted each time the page is reloaded.

<script src="myApp.js?random=<?php echo uniqid(); ?>"></script>

which evaluates to:

Go ahead, refresh the page.

The key part of the above method is the query string beginning with the question mark. If this is different from what’s cached, the browser will reload the resource. I ended up using a less dynamic approach that preserved the cache until the next release. I modified a statement like the one above and instead provided the tagged version as the parameter. For each new release, I just incremented the version number. This forces a cache bust for each release. This is a well-documented approach, but by writing this post I hope I can save one person the pain of forgetting to cache bust a critical application. Thanks for reading.

That One Time I Didn’t Cache Bust