Caching Explained: When It Makes Your App Faster and When It Doesn’t

Imagine a product page that needs the same category information on thousands of requests every hour.

The category changes perhaps once a day, yet every request asks the database for it again. The database performs the lookup, sends the result back, and the application repeats the process for the next user.

Caching can remove most of that repeated work.

Store the result somewhere faster after the first lookup, reuse it for later requests, and return to the database only when the cached value expires or changes.

That is the basic idea behind caching: keep a reusable copy of data or a computed result closer to where it will be needed so the application does not repeatedly pay the full cost of producing it.

AWS describes caching as particularly useful for frequently accessed data and read-heavy workloads, where it can reduce latency, improve throughput and lower pressure on the underlying data store. AWS Well-Architected: Implement Data Access Patterns That Utilize Caching

But caching is not automatically a performance improvement.

If the data changes constantly, is rarely requested twice, must always be current, or costs almost nothing to retrieve in the first place, a cache can add complexity without removing meaningful work.

For Levelworks, caching is therefore one technique for maintaining Performant App Health—not a default architectural requirement.

Caching works best when expensive work is repeated

The strongest caching candidates usually share three characteristics:

  1. the same data or result is requested repeatedly;
  2. retrieving or calculating it has meaningful cost;
  3. the result can safely be reused for some period.

Examples might include:

  • product or reference information;
  • configuration;
  • frequently requested API responses;
  • expensive aggregates;
  • generated reports;
  • static web resources.

Redis's current cache-aside guidance describes exactly this pattern: read-heavy applications can check the cache first and fall back to the primary database only on a miss, reducing database load in proportion to how often requests are served from cache. Redis: Cache-Aside

Suppose an expensive database query takes 300 milliseconds and its result is requested hundreds of times between updates.

That is a strong candidate.

If another query already completes in two milliseconds and every request asks for different data, caching it may accomplish very little.

The question is not:

Can this data be cached?

Most things technically can.

The useful question is:

What repeated expensive operation will this cache eliminate?

Cache hit rate determines how much work you actually avoid

A cache helps only when requests find what they need there.

That is a cache hit.

When the requested item is absent or expired, the application experiences a cache miss and must retrieve or calculate the original result.

Suppose 10,000 requests reach an application:

  • 9,000 are cache hits;
  • 1,000 reach the database.

The cache has removed most of the database work.

Now imagine only 1,000 requests are hits while 9,000 still reach the database. You have added another system to operate without substantially reducing the underlying workload.

AWS recommends monitoring cache efficiency and notes that low hit rates may indicate either insufficient cache capacity or an access pattern that does not benefit much from caching.

Cloudflare makes the same principle visible for web caching: its Cache Analytics separates requests served from cache from those reaching the origin, allowing teams to identify resources that frequently miss or are not cacheable. Cloudflare: Cache Analytics

Measure the cache rather than assuming its presence makes the application faster.

Caching is especially effective for static web resources

Browsers and CDNs provide a familiar example.

JavaScript, CSS, images and other resources often remain unchanged between requests. Re-downloading and regenerating them every time wastes network and server resources.

HTTP caching allows a previously stored response to be reused. MDN notes that this can improve response speed while also reducing work at the origin server because the request may not need to reach the application at all. MDN: HTTP Caching

Resources whose contents do not change are particularly straightforward to cache.

Dynamic application data is harder because freshness becomes part of the decision.

Caching becomes difficult when data changes frequently

Consider inventory.

The database says there is one item left.

That value is cached for ten minutes.

The item sells, but another customer continues seeing the cached quantity.

The application became faster while becoming less correct.

This is the central caching trade-off:

performance versus freshness.

For each cached value, decide how much staleness is acceptable.

A blog category may safely remain cached for hours.

A feature configuration might tolerate a few minutes.

Available inventory may require much tighter control.

An account balance may be inappropriate for the same caching strategy altogether.

AWS explicitly identifies frequently changing data and failure to consider consistency as common caching anti-patterns.

A cache is useful only when the application's freshness requirements allow reuse.

Invalidation is where much of caching complexity appears

Once the original data changes, the application needs to prevent an outdated cached copy from surviving indefinitely.

Common approaches include:

  • time-to-live (TTL): automatically expire the value after a period;
  • invalidate on write: remove the cached copy when authoritative data changes;
  • versioned resources: change the cache key or URL when content changes;
  • revalidation: ask whether a previously cached representation is still current.

Redis's documentation describes invalidation as fundamental to keeping cached copies synchronized with their authoritative source.

For HTTP resources, MDN documents mechanisms such as Cache-Control, ETag and Last-Modified for controlling freshness and revalidation.

If the invalidation rules become more complicated than the workload justifies, the cache may be solving the wrong problem.

[Internal Link: “How Database Performance Affects the Speed of Your App”]

Caching can make performance worse during a stampede

A cache can even amplify load under the wrong conditions.

Imagine one extremely popular key expires.

Hundreds of concurrent requests arrive immediately afterwards. Every request sees a miss and independently queries the primary database to regenerate the same value.

Instead of protecting the database, the expiration creates a sudden burst of repeated work.

This is often called a cache stampede.

Redis's cache-aside guidance explicitly describes this risk and demonstrates coordinating concurrent misses so only one request regenerates the value while others wait for the newly populated cache.

High-traffic applications should therefore consider not only how cached data expires, but what happens at the moment it does.

Do not treat a cache as the source of truth

A performance cache should normally contain data that can be reconstructed from an authoritative system.

AWS warns against relying on cached data as though it were durable primary storage.

If the cache disappears, the application should know how to obtain the underlying value again.

That distinction keeps performance optimization from quietly becoming a data-durability dependency.

A simple test for whether caching is appropriate

Before introducing a cache, ask:

Question

Good sign for caching

Is the same result requested repeatedly?

Yes

Is producing it meaningfully expensive?

Yes

Can the result safely remain unchanged for some period?

Yes

Will enough requests become cache hits?

Yes

Can invalidation be defined clearly?

Yes

Can the source data be reconstructed if the cache disappears?

Yes

If several answers are no, investigate another optimization first.

Perhaps the underlying query is inefficient. Maybe the application is requesting unnecessary data, or the service is performing expensive work that should simply be removed.

[Internal Link: “Application Performance Optimization: A Practical Guide”]

Caching should not become a way of hiding a fundamentally poor access pattern.

Measure the result after adding the cache

Once caching is introduced, monitor:

  • hit and miss rate;
  • latency for hits versus misses;
  • load on the underlying database or service;
  • eviction and expiration behaviour;
  • stale-data incidents;
  • memory consumption.

Then compare the user-facing workflow with the original baseline.

If database load falls dramatically but user latency barely changes, perhaps the database was not the dominant bottleneck.

If hit rates remain extremely low, the workload may simply not contain enough reuse.

[Internal Link: “Why Is My App Slow? 10 Common Causes of Poor Application Performance”]

Caching is valuable when it removes real work

Caching can be one of the most effective application-performance techniques because it avoids doing the same expensive work repeatedly.

But the performance benefit depends on the workload.

Frequently reused, expensive-to-retrieve and reasonably stable data can be excellent cache material. Rapidly changing, rarely repeated or correctness-sensitive data may not be.

For Levelworks, maintaining Performant App Health means choosing caching because measurement shows that it removes meaningful latency or load—not because caching is assumed to make every architecture faster.

The decision ultimately comes down to three questions:

How expensive is the original work? How often can its result be reused? And how stale can that result safely become?

If those answers are favorable, caching can make an application substantially faster.

If they are not, the cache may simply become another thing the application has to maintain.

‍

Continue Reading