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:
- 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.
- 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.jsorstyle.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:
{{- $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:
{{- $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. ☕
Related posts
The AI Coding Agent Dictionary: Colloquial Prompts vs Standard Tech Terms
A battle-tested dictionary translating everyday developer prompts into precision architectural terms for AI coding agents to generate robust code.
High-Leverage Coding Terms & Jargon for AI Coding Agents
Discover the architectural keywords and high-leverage engineering jargon that turn AI coding agents into precision tools, eliminating hallucinations.
What AI DevKit is: a control plane for AI coding agents — and where it stops
An architectural guide to AI DevKit: local-first CLI/TUI console, shared config, SQLite memory, and workflow skills for managing multiple coding agents.
Free Claude Code Explained: One Local Proxy for 50+ AI Providers
Free Claude Code routes Claude Code, Codex, Pi, and eight more agents through one local proxy with 50 ToS-friendly providers and automatic fallback.