What a Cache Really Is

What qualifies as a cache, what does not, and why precise boundaries matter before choosing any caching pattern.

A cache is a fast, disposable answer stored closer to the thing that needs it than the original source of truth. Sometimes that answer is a copied value such as a product record. Sometimes it is a rendered response, a derived computation, or a partially assembled view model. What makes it a cache is not the product name or the storage engine. What makes it a cache is the contract: it is secondary, reusable, and allowed to be temporarily behind the authoritative state.

That definition matters because teams often call several different mechanisms “cache” when they are doing different jobs. A primary database is not a cache. A read replica is usually not a cache because it is still part of the persistence layer and participates in durability and recovery design. An index is not a cache because it is part of how the primary system answers queries. A queue is not a cache because its purpose is work coordination, not reuse. A CDN edge cache usually is a cache, but it is a specialized one built around distribution and content reuse rather than arbitrary object access.

    flowchart LR
	    A["Source of truth\n(database or service)"] --> B["Cache entry\nfast reusable answer"]
	    B --> C["Caller"]
	    A -. "misses, refreshes,\nor invalidation" .-> B
	    D["Other mechanisms"] --> E["Replica"]
	    D --> F["Index"]
	    D --> G["Queue"]
	    D --> H["Log"]
	    E -. "not primarily for reuse" .-> A
	    F -. "part of query execution" .-> A
	    G -. "coordinates work" .-> C
	    H -. "observes activity" .-> C

What to notice:

  • the cache exists because repeated access is expected
  • the cache depends on another authority to populate or validate it
  • the core design question is when the system may trust the cached answer

Why It Matters

A weak definition of caching leads directly to weak design decisions. If a team cannot say whether a layer is authoritative or merely opportunistic, they will eventually make the wrong durability, invalidation, or fallback assumption. The result is often one of two failure modes:

  • the cache is treated like the database, so stale answers quietly become product truth
  • the cache is treated as disposable decoration, so the system is never designed to recover cleanly when the cache is wrong or empty

That is why strong caching work starts with a boundary question: what is the real authority, and under what conditions is the cached answer acceptable?

A Cache Is About Reuse, Not Storage Technology

Teams sometimes assume Redis means “cache” and PostgreSQL means “source of truth.” That shortcut is dangerous. A Redis key used as a durable workflow checkpoint is not really a cache. A PostgreSQL materialized view that is periodically regenerated may behave like one. The category depends on purpose and trust assumptions, not vendor choice.

The same technology can serve both roles:

  • an in-memory map inside one process can be a cache for expensive computations
  • the same process memory can also hold critical session state that should not have been ephemeral
  • an object store can act as durable source data in one design and a generated artifact cache in another

The right mental model is therefore functional, not infrastructural. Ask what the layer exists to optimize and what happens if it disappears.

Source Of Truth vs Cached Representation

The source of truth owns correctness. The cache owns convenience. That difference shapes every downstream decision:

  • writes usually flow to the source of truth first
  • reads may be served by the cache when the freshness risk is acceptable
  • invalidation or refresh logic exists because the cache is not self-authenticating

In a healthy design, the cache can be dropped and rebuilt. That rebuild may be painful, expensive, or slow, but it should be possible. If losing the layer causes irreversible business loss, then the layer is probably doing more than caching.

Example

This example uses a small TypeScript cache-aside helper. The important point is not the code style. The important point is that the cache is explicitly secondary to the catalog service.

 1type Product = {
 2  id: string;
 3  name: string;
 4  price: number;
 5  updatedAt: string;
 6};
 7
 8interface Cache {
 9  get(key: string): Promise<string | null>;
10  set(key: string, value: string, ttlSeconds: number): Promise<void>;
11}
12
13async function getProduct(cache: Cache, productId: string): Promise<Product> {
14  const key = `product:${productId}`;
15  const cached = await cache.get(key);
16
17  if (cached) {
18    return JSON.parse(cached) as Product;
19  }
20
21  const product = await fetchFromCatalogService(productId);
22  await cache.set(key, JSON.stringify(product), 60);
23  return product;
24}

What to notice:

  • the function still works without the cache, although it becomes slower
  • the authoritative read comes from fetchFromCatalogService
  • the TTL is an explicit freshness bet, not a correctness guarantee

Common Mistakes

  • calling any fast storage layer a cache without defining authority
  • storing data in the cache that cannot be safely regenerated
  • assuming that because a value was cached recently, it is still correct now
  • forgetting that different readers may need different freshness budgets
  • treating cache keys as an implementation detail even when they encode business identity

Design Review Question

If a system loses its entire cache cluster and the team says, “We would lose customer data,” what does that reveal about the design?

The stronger answer is that the system has probably allowed the cache layer to become authoritative. A real cache may be operationally important, but it should remain reconstructable from some more durable or more authoritative system state.

Quiz Time

Loading quiz…
Revised on Thursday, April 23, 2026