All templates
Flowchart template

Caching strategy decision tree

Choose between CDN, app cache, database cache, or hybrid approaches.

Caching is a classic tradeoff: faster reads, but stale data. This diagram helps you navigate the choices by asking the right questions: Does this data change often? Is it read-heavy? Should it live on the CDN, in Redis, in your app's memory, or at the database?

The decision tree starts with change frequency: static data (rarely changing) gets a long time-to-live (TTL) and can live on a CDN or in memory. Frequently-changing data needs a shorter TTL or event-driven refresh. Then it splits on read patterns: if reads dominate, a cache layer in front of the database (like Redis) is worth the operational cost. If writes are common, a shorter TTL or cache invalidation strategy is better.

When to use this template

  • Performance optimization design — when you're debugging slow queries or high database load, this diagram helps you decide whether to add caching and where.
  • Architecture review — when teams have inconsistent caching practices (some use Redis, some use app memory, some don't cache at all), this gives everyone a common framework.
  • New feature planning — as you add a new endpoint or data access pattern, sketch it through this decision tree to decide the caching strategy upfront.

How to adapt it

The framework applies to any data layer, but the concrete implementation depends on your tech stack and scale:

  • Add a cost vs. latency node: CDNs cost per GB, Redis costs per memory-unit, database query caches cost in CPU. Quantify the tradeoff for your use case.
  • Insert a cache invalidation strategy after choosing the cache layer: TTL (time-based), event-driven (invalidate on writes), or tag-based (invalidate all cached data for a resource).
  • Add a monitoring branch that tracks cache hit rates and hit latency — high miss rates mean your TTL is too aggressive; slow hits mean your cache layer is bottlenecked.

Visual edits regenerate clean code, so you can iterate on this strategy as your performance profile changes.

Mermaid code

Copy it anywhere Mermaid is supported — GitHub, Notion, or your docs.

flowchart TD
    A[Data access needed] --> B{Data changes frequently?}
    B -->|Rarely| C[Cache aggressively]
    B -->|Often| D{Read-heavy?}
    C --> E{Static assets<br/>or HTML?}
    E -->|Yes| F[Use CDN<br/>long TTL]
    E -->|No| G[Use app memory<br/>or Redis]
    D -->|Yes| H[Database query cache]
    D -->|No| I[Short TTL or<br/>event-driven refresh]
    H --> J{Cache layer<br/>needed?}
    I --> J
    J -->|Yes| K[Add Redis<br/>in front]
    J -->|No| L[Keep in-app cache]
    F --> M[Monitor cache hits]
    G --> M
    K --> M
    L --> M

Frequently asked questions

What is a caching strategy?
It's a decision framework for where and how long to cache data. Each layer — CDN, app, database — has different tradeoffs: CDN is fast and global but can't know about freshness; Redis is flexible but adds operational complexity; app in-memory cache is simple but doesn't scale across instances. A strategy picks the right layer for each type of data based on how often it changes and how frequently it's read.
When should I use a CDN instead of Redis?
Use a CDN for static assets, HTML, and API responses that don't change for hours or days — blogs, product pages, public datasets. The CDN sits at the edge (geographically closer to users) and requires no backend code. Use Redis for frequently-read, short-lived data — user sessions, feature flags, leaderboards — where you need flexibility and freshness.
What does 'database query cache' mean?
Most databases (PostgreSQL, MySQL) have query caches that store results of frequent queries. But it's often disabled in production because the invalidation logic is fragile — add one row and the cache invalidates globally. Instead, cache at the application layer (Redis) or use read replicas with eventual consistency. The diagram shows this as a deliberate choice, not an assumption.
How do I avoid cache stampedes?
A cache stampede happens when popular cached data expires and 1000 requests hit the database at once trying to refill the cache. Add jitter to TTLs so expirations are staggered, use cache warming (refresh before expiry), or implement a 'probabilistic expiration' where a small percentage of requests trigger a refresh even if the cache is still valid. Visual edits regenerate clean code, so you can annotate this diagram with your specific strategies.

Related templates