Featured image of post Why Is Production Still Running My Old Code?

Why Is Production Still Running My Old Code?

Deployed your latest changes but still seeing the ghost of the old version? Let's talk about the aggressive memory of browsers and how to fix it.

I once spent 30 minutes refreshing my browser, clearing my cache, and even incognito-moding my way into madness because my “simple CSS change” just to change a button color refused to show up in production.

Locally? Perfect. Production? Using the code from last week.

If you’ve ever deployed to Firebase, Vercel, or Netlify and shouted “BUT IT WORKS ON MY LOCALHOST!” at your screen, this post is for you.

The Forgetful Barista Analogy

Imagine a busy coffee shop. You go there every single morning and order “The Usual” (a large oat latte).

The barista knows you. As soon as you walk in, they grab a pre-made cup from the counter that they made 5 minutes ago because they knew you were coming. It’s fast. It’s efficient.

But today, you decided to change your order. You want “The Usual… but with caramel”.

You walk in, say “The Usual please!”, and boom—they hand you the pre-made plain latte.

“No,” you say, “I changed my order!” “Sorry,” they say, “The ticket just said ‘The Usual’, so I gave you the one on the counter.”

This is Caching.

But wait, there are two layers of people refusing to give you new coffee:

  1. Your Brain (The Browser): You remember you had coffee on your desk from yesterday. You might just drink that instead of going to the shop.
  2. The Store Manager (The CDN): Even if you go to the shop, the Manager (Cloudflare/Firebase) stands at the door with a tray of pre-made “Usual” coffees to hand out instantly. They don’t even let you talk to the Barista (The Server).
  • You = The Browser
  • The Manager = The CDN (Cloudflare/Firebase)
  • The Barista = The Origin Server
  • “The Usual” = main.js or style.css

When your website asks for main.js, the CDN looks in its warehouse and says: “Oh, I have 1,000 copies of main.js from yesterday. Sending one down!”

The request never even reaches your server. The CDN intercepts it. It doesn’t know you changed the contents of the file. It only checks the name.

The Solution: Change the Order Name

How do you get the correct coffee? You don’t order “The Usual”. You order “Order #90210”.

If tomorrow you want something different, you order “Order #90211”.

The barista cannot give you the old cup, because the order number on the cup doesn’t match the new number you just asked for. They are forced to make a fresh one.

In web development, we call this Asset Fingerprinting (or Cache Busting).

Instead of telling the browser to load main.js, we tell it to load main.abc12345.js.

When we change the code, we change the filename: main.xyz98765.js. The browser sees a filename it has never seen before, so it must download the new file.

Fixing It in Hugo

If you are using Hugo, you don’t have to rename files manually (thank goodness). Hugo can do it for you automatically using resources.Fingerprint.

I had this exact code in my footer:

1
2
{{- $script := resources.Get "ts/main.ts" | js.Build $opts -}}
<script src="{{ $script.RelPermalink }}"></script>

This generated a file named main.js. Boring. Cacheable. Dangerous.

I changed it to this:

1
2
{{- $script := resources.Get "ts/main.ts" | js.Build $opts | resources.Fingerprint "sha256" -}}
<script src="{{ $script.RelPermalink }}" integrity="{{ $script.Data.Integrity }}"></script>

Now, Hugo generates something like: /ts/main.50550e6c64a7ed...2afc66aa.js

Every time I change even a single character in my main.ts, that hash string changes. New hash = New filename = Fresh code.

The Takeaway

Browser caching is your friend for speed, but your enemy for updates.

Don’t trust “Hard Refresh” (Ctrl+F5). It might fix it for you, but your users won’t know to do that. They’ll just see a broken site.

Always fingerprint your assets. Make the barista make it fresh. ☕

Made with laziness love 🦥

Subscribe to My Newsletter