Explain the concept of caching and how it can be used to improve performance
TL;DR
Caching reuses a previously computed or fetched result to reduce latency, bandwidth, and server work. A cache is correct only when its key, freshness policy, and invalidation behavior match the data. For versioned static assets, long-lived HTTP caching is effective; personalized or frequently changing API data usually needs revalidation or a shorter lifetime. Service workers add offline and custom routing capabilities, but also add another cache that must be updated deliberately.
Cache lookup and revalidation
A cache improves repeat access only when its freshness and invalidation rules let it answer safely.
Caching trades freshness and invalidation complexity for lower latency, network use, or computation, so the policy matters as much as the storage layer.
The concept of caching and how it can be used to improve performance
What is caching?
Caching is a technique used to store copies of files or data in a temporary storage location, known as a cache, to reduce the time it takes to access them. The primary goal of caching is to improve performance by minimizing the need to fetch data from the original source repeatedly.
Types of caching
Browser cache
The browser cache stores copies of web pages, images, and other resources locally on the user's device. When a user revisits a website, the browser can load these resources from the cache instead of fetching them from the server, resulting in faster load times.
Service workers
Service workers are scripts that run in the background and can intercept network requests. They can cache resources and serve them from the cache, even when the user is offline. This can improve performance and provide a better user experience.
HTTP caching
HTTP caching involves using HTTP headers to control how and when resources are cached. Common headers include Cache-Control, Expires, and ETag.
How caching improves performance
Reduced latency
By storing frequently accessed data closer to the user, caching reduces the time it takes to retrieve that data. This results in faster load times and a smoother user experience.
Reduced server load
Caching reduces the number of requests made to the server, which can help decrease server load and improve overall performance.
Offline access
With service workers, cached resources can be served even when the user is offline, keeping the application usable without a network connection.
Implementing caching
HTTP caching example
For a content-hashed asset such as /assets/app.a1b2c3.js, the server can send:
Cache-Control: public, max-age=31536000, immutable
When the content changes, the build emits a new URL. By contrast, an authenticated account response should not receive this policy. It might use Cache-Control: private, no-cache so the browser may store it but must revalidate before reuse, or no-store when storage is inappropriate.
Service worker example
const CACHE_NAME = 'app-shell-v2';self.addEventListener('install', (event) => {event.waitUntil(caches.open(CACHE_NAME).then((cache) => {return cache.addAll(['/index.html', '/styles.css', '/app.js']);}),);});self.addEventListener('fetch', (event) => {event.respondWith(caches.match(event.request).then((cachedResponse) => cachedResponse || fetch(event.request)),);});
This cache-first strategy suits versioned static assets, but can serve stale API data. A production service worker also needs version cleanup, update handling, error behavior, and a strategy chosen per request type. Libraries such as Workbox can implement these patterns, but do not remove the need to define freshness rules.
Cache failure modes
- Stale data: A long lifetime or cache-first strategy returns an outdated result.
- Incorrect keys: A cache omits a locale, authorization scope, or query parameter and returns one user's variant to another context.
- Unbounded growth: An in-memory cache has no size or time-based eviction.
- Cache stampede: Many misses recompute the same expensive result simultaneously.
- Sensitive data retention: Private responses are stored in a shared or persistent cache.
Measure hit rate and user-visible latency. A cache with few hits or expensive invalidation can add complexity without improving performance.