100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
YAML

Caching and Build Artifacts

Learn how CI/CD pipelines speed up builds with dependency caching and pass data between jobs using artifacts, and where each technique fits.

Build & Test AutomationIntermediate9 min readJul 8, 2026
Analogies

Caching and Build Artifacts

As pipelines grow, redundant work becomes the biggest tax on developer velocity. Every pipeline run that reinstalls the same npm packages, redownloads the same Maven dependencies, or recompiles unchanged code from scratch wastes minutes that add up across hundreds of daily runs. Caching and artifacts are the two mechanisms CI/CD systems provide to avoid this waste, but they solve different problems and are frequently confused. Caching persists reusable inputs — dependency trees, compiled toolchains, package manager stores — across separate pipeline runs so the next run can skip expensive downloads or installs. Artifacts, by contrast, carry outputs of one job forward to another job within the same pipeline run, such as passing a compiled binary from a build job to a deploy job, or a test report from a test job to a publish job. Understanding this distinction is essential to designing pipelines that are both fast and correct.

🏏

Cricket analogy: Virat Kohli doesn't re-learn his stance before every net session (cached muscle memory), but the specific scorecard from today's match against Australia is a fresh output carried to the next day's team review — caching skips setup, artifacts carry results forward.

How Dependency Caching Works

Most CI platforms implement caching as a key-value blob store scoped to the repository or branch. You supply a cache key, typically derived from a hash of a lockfile (package-lock.json, poetry.lock, Gemfile.lock), and a set of paths to persist (node_modules, ~/.m2, ~/.cache/pip). On a cache hit, the platform downloads and restores those paths before your build steps run, skipping the installation step entirely or making it a no-op. On a cache miss — for example when the lockfile changes — the pipeline falls back to a fresh install and then saves a new cache entry under the new key. Well-designed cache keys include a restore-fallback chain: an exact match on the lockfile hash first, then a broader prefix match, so a partial cache is still better than none when dependencies shift slightly.

🏏

Cricket analogy: A groundskeeper preps the pitch to a spec matching the toss report (cache key = pitch report); if conditions match exactly, the same roller and grass length are reused (hit), but if weather shifts they fall back to the nearest similar preparation rather than starting from bare earth.

How Build Artifacts Work

Artifacts are explicit outputs uploaded by one job and downloaded by another, and unlike caches they are treated as the authoritative result of that run rather than a performance optimization. A build job compiles source into a binary or bundle, uploads it as a named artifact, and a downstream deploy job downloads that exact artifact rather than rebuilding it. This guarantees that what gets tested is exactly what gets deployed — a principle often called 'build once, deploy everywhere.' Artifacts are also commonly used to retain test reports, coverage output, and logs for post-run inspection, and most platforms let you configure a retention period after which they are automatically deleted to control storage costs.

🏏

Cricket analogy: The exact bat Rohit Sharma gets inspected and certified by umpires before the match is the one he must use throughout — you don't swap in an uncertified replacement mid-innings; certified equipment carries forward untouched, and old certification stickers are eventually retired.

yaml
name: build-and-cache
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Cache node_modules
        id: npm-cache
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: dist-bundle
          path: dist/
          retention-days: 5

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download build artifact
        uses: actions/download-artifact@v4
        with:
          name: dist-bundle
          path: dist/
      - name: Deploy
        run: ./scripts/deploy.sh dist/

Think of caching like keeping ingredients pre-chopped in your fridge for tomorrow's meal — it speeds up a similar but separate cooking session. An artifact is more like handing a finished dish from the kitchen straight to the person plating it in the very same meal service — it must be the exact thing, not a fresh approximation.

A common pitfall is caching build output itself (e.g. the dist/ folder) instead of dependencies, and then trusting a stale cache hit to skip the actual build step. Caches are best-effort and can be evicted or restored inconsistently across runners, so never treat a cache as a substitute for reproducible builds — only cache inputs that are safe to regenerate deterministically.

Cache Invalidation and Storage Limits

Cache invalidation is deliberately manual in most systems: you control it entirely through the cache key. If the key never changes, a corrupted or outdated cache can silently persist for months. Most platforms also enforce per-repository storage quotas and evict the least-recently-used caches once the limit is reached, so teams with many branches should scope cache keys carefully (often including a branch or job-matrix identifier) to avoid thrashing between unrelated cache entries competing for the same slot.

🏏

Cricket analogy: If a groundskeeper never updates a watering label after switching soil mix, the pitch report silently misleads the captain for months; grounds also have limited storage for prepared pitches, so the oldest unused one gets dug up first when a new one is needed.

  • Caching persists reusable inputs (dependencies, toolchains) across separate pipeline runs to save time.
  • Artifacts carry outputs (binaries, reports) between jobs within a single pipeline run.
  • Cache keys should be derived from lockfile hashes with fallback restore-keys for partial hits.
  • Artifacts enforce 'build once, deploy everywhere' by ensuring tested and deployed outputs are identical.
  • Caches are best-effort and evictable — never rely on them for correctness, only for speed.
  • Both caching and artifacts have retention/storage limits that require lifecycle management.

Practice what you learned

Was this page helpful?

Topics covered

#YAML#CICDToolsPipelinesStudyNotes#DevOps#CachingAndBuildArtifacts#Caching#Build#Artifacts#Dependency#StudyNotes#SkillVeris