CI Cache Optimization Playbook
How to reduce pipeline time by separating dependency cache and build artifact cache

Introduction
If CI time becomes longer, not only development productivity but also deployment stability decreases. If you just turn on the cache, it may seem like it will go faster, but if the key design is wrong, it will actually lead to an unstable build. This article summarizes a practical playbook that increases CI cache efficiency while maintaining reliability.

Problem definition
Cache optimization is a matter of balancing speed and reproducibility.
- The cache key range is wide, so even minor changes will invalidate the entire cache.
- There is a high possibility of contamination by mixing the dependency cache and build output cache.
- The optimization effect cannot be measured because the cache hit rate is not observed.
The key is to divide the cache into tiers. Dependencies, build intermediate results, and test results must be managed separately.
Key concepts
| perspective | Design criteria | Verification points |
|---|---|---|
| key design | Includes lock file + runtime version | Cache miss cause classification |
| Layering | deps/build/test cache isolation | Contamination reproduction rate |
| Observation | hit ratio dashboard | Average Pipeline Time |
| Summary | TTL based cache cleaning | storage cost |
Speed improvements are maintained only when there are measurable indicators. Tracking cache hit and miss rates together can help prevent overoptimization.
Code Example 1: GitHub Actions Cache Key
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pnpm-\${{ runner.os }}-
- name: Cache Next build
uses: actions/cache@v4
with:
path: .next/cache
key: next-\${{ runner.os }}-\${{ hashFiles('**/*.ts', '**/*.tsx', 'next.config.ts') }}
Code example 2: Collecting cache metrics
export function logCacheMetrics(input: { step: string; hit: boolean; durationMs: number }) {
console.log(
JSON.stringify({
metric: "ci_cache",
step: input.step,
hit: input.hit,
duration_ms: input.durationMs,
timestamp: new Date().toISOString(),
}),
);
}
Architecture flow
Tradeoffs
- Narrowing the cache range increases stability, but reduces the maximum speed improvement.
- Leaving restore-keys wider increases the hit rate, but there is a risk of loading old caches.
- As the cache layer increases, setup complexity increases, but debugging difficulty decreases.
Cleanup
CI cache optimization should aim for reproducible builds rather than fast builds. By combining a tiered cache strategy and metric-based tuning, you can achieve both stability and speed.
Image source
- Cover: source link
- License: CC BY-SA 3.0 / Author: Bernard Gagnon
- Note: After downloading the free license image from Wikimedia Commons, it was optimized to JPG at 1600px.