Advanced

How to Design and Implement Caching Strategies with Claude Code

Learn how to design and implement caching strategies using Claude Code. Includes practical code examples and step-by-step guidance.

Why Use Claude Code for Cache Strategy Design

Caches that aren’t designed carefully lead to stale data being shown or memory being wasted. Claude Code can analyze your application’s data access patterns and suggest (and implement) an optimal caching strategy.

Implementing a Redis Cache

> Implement a cache layer using Redis.
> Support cache invalidation, TTL management, and the cache-aside pattern.
// src/lib/cache.ts
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!);

interface CacheOptions {
  ttl?: number;      // seconds
  prefix?: string;
}

export class CacheService {
  private defaultTTL = 300; // 5 minutes

  async get<T>(key: string, options?: CacheOptions): Promise<T | null> {
    const fullKey = this.buildKey(key, options?.prefix);
    const data = await redis.get(fullKey);
    if (!data) return null;

    try {
      return JSON.parse(data) as T;
    } catch {
      return null;
    }
  }

  async set<T>(key: string, value: T, options?: CacheOptions): Promise<void> {
    const fullKey = this.buildKey(key, options?.prefix);
    const ttl = options?.ttl ?? this.defaultTTL;

    await redis.setex(fullKey, ttl, JSON.stringify(value));
  }

  async getOrSet<T>(
    key: string,
    fetcher: () => Promise<T>,
    options?: CacheOptions
  ): Promise<T> {
    const cached = await this.get<T>(key, options);
    if (cached !== null) return cached;

    const data = await fetcher();
    await this.set(key, data, options);
    return data;
  }

  async invalidate(key: string, prefix?: string): Promise<void> {
    const fullKey = this.buildKey(key, prefix);
    await redis.del(fullKey);
  }

  async invalidatePattern(pattern: string): Promise<void> {
    const keys = await redis.keys(pattern);
    if (keys.length > 0) {
      await redis.del(...keys);
    }
  }

  private buildKey(key: string, prefix?: string): string {
    return prefix ? `${prefix}:${key}` : key;
  }
}

export const cache = new CacheService();

Implementing the Cache-Aside Pattern

// src/services/product-service.ts
import { cache } from '@/lib/cache';
import { prisma } from '@/lib/db';

export class ProductService {
  async getProduct(id: string) {
    return cache.getOrSet(
      `product:${id}`,
      () => prisma.product.findUnique({
        where: { id },
        include: { category: true, reviews: { take: 10 } },
      }),
      { ttl: 600, prefix: 'products' }
    );
  }

  async getPopularProducts(limit = 20) {
    return cache.getOrSet(
      `popular:${limit}`,
      () => prisma.product.findMany({
        orderBy: { salesCount: 'desc' },
        take: limit,
        include: { category: true },
      }),
      { ttl: 300, prefix: 'products' }
    );
  }

  async updateProduct(id: string, data: UpdateProductInput) {
    const product = await prisma.product.update({
      where: { id },
      data,
    });

    // Invalidate related caches
    await cache.invalidate(`product:${id}`, 'products');
    await cache.invalidatePattern('products:popular:*');

    return product;
  }
}

Setting HTTP Cache Headers

// src/middleware.ts
import { NextResponse, NextRequest } from 'next/server';

export function middleware(req: NextRequest) {
  const res = NextResponse.next();
  const path = req.nextUrl.pathname;

  // Static assets: long-lived cache
  if (path.match(/\.(js|css|png|jpg|svg|woff2)$/)) {
    res.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
  }

  // API: no caching
  if (path.startsWith('/api/')) {
    res.headers.set('Cache-Control', 'no-store');
  }

  // Pages: short cache + ISR
  if (!path.startsWith('/api/') && !path.match(/\.[a-z]+$/)) {
    res.headers.set('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=300');
  }

  return res;
}

In-Memory Caching (Process-Local)

In-memory caching is effective for small amounts of data.

// src/lib/memory-cache.ts
interface CacheEntry<T> {
  value: T;
  expiresAt: number;
}

export class MemoryCache {
  private store = new Map<string, CacheEntry<unknown>>();
  private maxSize: number;

  constructor(maxSize = 1000) {
    this.maxSize = maxSize;
  }

  get<T>(key: string): T | null {
    const entry = this.store.get(key);
    if (!entry) return null;

    if (Date.now() > entry.expiresAt) {
      this.store.delete(key);
      return null;
    }

    return entry.value as T;
  }

  set<T>(key: string, value: T, ttlMs: number): void {
    // Enforce size limit
    if (this.store.size >= this.maxSize) {
      const firstKey = this.store.keys().next().value;
      if (firstKey) this.store.delete(firstKey);
    }

    this.store.set(key, {
      value,
      expiresAt: Date.now() + ttlMs,
    });
  }

  clear(): void {
    this.store.clear();
  }
}

// Use for frequently accessed data like configuration values
export const configCache = new MemoryCache(100);

Cache Strategy Selection Guide

Data typeRecommended cacheTTL guideline
Configuration masterMemory + Redis1 hour
User profileRedis10 minutes
Product listRedis + CDN5 minutes
Session infoRedis24 hours
Static assetsCDN1 year
API responsesHTTP cache1 minute

Cache Monitoring

// Measure cache hit rate
export class CacheMetrics {
  private hits = 0;
  private misses = 0;

  recordHit() { this.hits++; }
  recordMiss() { this.misses++; }

  getHitRate(): number {
    const total = this.hits + this.misses;
    return total === 0 ? 0 : this.hits / total;
  }

  getStats() {
    return {
      hits: this.hits,
      misses: this.misses,
      hitRate: `${(this.getHitRate() * 100).toFixed(1)}%`,
    };
  }
}

Summary

With Claude Code, you can efficiently design and implement multi-layer caching strategies including Redis, HTTP, and in-memory caches. Writing your caching policy into CLAUDE.md keeps the implementation consistent across the project. For a broader look at performance improvements, see productivity tips that 3x your output.

For more on Claude Code, see the official Anthropic documentation. For Redis, see the official Redis documentation.

#Claude Code #caching #Redis #performance #design

Level up your Claude Code workflow

50 battle-tested prompt templates you can copy-paste into Claude Code right now.

Free

Free PDF: Claude Code Cheatsheet in 5 Minutes

Key commands, shortcuts, and prompt examples on a single printable page.

Download PDF
M

About the Author

Masa

Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.