January 8, 2025

The Cache Introduction I Wish I Had

Caching is often a blessing and a curse, but is always essential for better web performance.

Caching. It’s one of those words that you hear thrown around in tech conversations, like "currying," “programmatic determinability,” or “structural design patterns” - everyone nods as if they totally get it. But for a long time, caching felt like magical fairy dust for performance on projects and my personal site...until it breaks, and suddenly it’s a giant headache. So, let’s dive into what caching actually is, why it’s essential, and how you can wield its power without breaking your keyboard.

What is Cache? #

Think of cache as your app’s short-term memory. It’s a temporary storage layer that keeps frequently accessed data so your app doesn’t have to go fetch it every single time someone asks for it. Imagine if you had to Google “how to boil water” every time you wanted tea. Annoying, right? That’s what cache prevents.

In practical terms, caching can happen at various levels:

The point is: cache saves time and resources. But, as you’ll see, it’s not all sunshine and rainbows.

Why Cache Matters #

Caching is like the secret seasoning of performance optimization. Whether you’re building a web app, pumping up your blog, or making sure your users can browse while sipping lattes at a Wi-Fi dead zone, caching is your best friend. Here are some reasons why:

self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('my-cache-v1').then((cache) => {
return cache.add('/styles.css'); // Cache your CSS file
})
);
});

Caching and Cookies #

Cookies are a sometimes-overlooked form of caching, particularly for small bits of user data. A favorite use case of mine is to save and/or check for any theme the user set on a website. This remembers your user’s preferences between visits and sessions.

// Function to get or set theme cookie
function getOrSetTheme(defaultTheme = 'dark') {
// Try to find existing theme cookie
const existingTheme = document.cookie
.split('; ')
.find(row => row.startsWith('theme='))
?.split('=')[1];

if (existingTheme) {
return existingTheme;
}

// If no theme cookie exists, set it
document.cookie = `theme=${defaultTheme}; path=/; expires=Fri, 31 Dec 9999 23:59:59 GMT`;
return defaultTheme;
}

const theme = getOrSetTheme();
console.log(theme); // Outputs: 'dark' (or existing theme if set)
// From here, you can add a class or custom property to your site

Good use cases for cookies:

Bad use cases for cookies:

Common Difficulties in Managing the Cache #

Caching is powerful, but it’s also like that one friend who’s amazing 90% of the time and a total disaster the other 10%. Here are some common issues ahead of time so you can better avoid them.

The Dreaded Cache Invalidation #

Ah, cache invalidation. Phil Karlton famously said, “There are only two hard things in Computer Science: cache invalidation and naming things.” It’s the most-used quote at every office I’ve been at for good reason. Cache invalidation is the process of ensuring outdated or “stale” data gets removed or updated correctly. Mess it up, and users might see old data or broken interfaces. Even worse, you may spend hours trying to debug a webpage only to see all your updates are correct, only to see they kept getting overrode by a stale cache.

Here’s an example of checking for a stale CSS file and updating the cache in a PWA:

self.addEventListener('activate', (event) => {
const cacheWhitelist = ['my-cache-v2']; // The new cache version
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (!cacheWhitelist.includes(cacheName)) {
return caches.delete(cacheName); // Delete old caches
}
})
);
})
);
});

Cache Size: Bigger Isn’t Always Better #

Caches can grow like that junk drawer in your kitchen—until they’re a bloated mess and your family yells at you. Overloaded caches can slow down your app or even crash it. To prevent this:

# Set up Redis connection
redis = Redis.new(host: "localhost", port: 6379)

# Cache data with a 1-hour expiration
redis.setex("user_preferences", 3600, { theme: "dark" }.to_json)

# Later, retrieve the data (returns nil if expired)
cached_data = redis.get("user_preferences")

if cached_data
preferences = JSON.parse(cached_data)
else
# Handle cache miss - fetch from database, etc.
end

Practical Tips for Managing Cache in PWAs #

Here are some rules of thumb for how you can keep your cache (and your sanity) in check whether it's for PWAs, browsers, or database requests.

Wrapping Up #

Caching is one of the most powerful tools in a developer’s arsenal for improving performance and user experience. But like any tool, it comes with its own set of challenges. By understanding the basics, leveraging best practices, and tackling common pitfalls head-on, you can make caching work for you—not against you.

So go ahead, version your caches, embrace service workers, and never forget to clear out the stale stuff. Happy caching!

Footnotes

  1. To be clear, this isn't me. Not since we got a Keurig.
  2. To my friends reading this, you know who you are.
  3. Your coworkers will at least let it go after a few days.