2 min read

CI Cache Optimization Playbook

How to reduce pipeline time by separating dependency cache and build artifact cache

CI Cache Optimization Playbook thumbnail

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.

CI 캐시 최적화 플레이북 커버
Wikimedia Commons 기반 무료 이미지

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

perspectiveDesign criteriaVerification points
key designIncludes lock file + runtime versionCache miss cause classification
Layeringdeps/build/test cache isolationContamination reproduction rate
Observationhit ratio dashboardAverage Pipeline Time
SummaryTTL based cache cleaningstorage 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

Mermaid diagram rendering...

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.

Comments