Copyright (c) 2026 MindMesh Academy. All rights reserved. This content is proprietary and may not be reproduced or distributed without permission.

2.3.1. Caching and Artifacts

💡 First Principle: Caching trades storage for time on things you could rebuild but would rather not; artifacts trade storage for access to things you actually need after the run. Get the intent right and the configuration follows.

actions/cache works on a key-and-fallback model:

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

An exact key hit is a cache hit; if none matches, restore-keys are tried as prefix matches, newest first, giving you a partial (still useful) cache — and the action then saves a new entry under the exact key at job end. The hashFiles() pattern is the idiom: the key changes only when the lockfile changes, which is exactly when the dependency tree changes.

Cache scoping rules are exam favorites: a cache created on a branch is readable by that branch and its child branches; the default branch's caches are readable by all branches; but sibling feature branches cannot read each other's caches. That's why the standard advice is to warm caches on main.

Artifacts use the upload/download pair, and v4 changed behavior meaningfully — artifacts are now immutable (you cannot append to an existing name; uploading the same name twice fails), each upload is a separate artifact, and downloads are available immediately rather than at run end:

- uses: actions/upload-artifact@v4
  with:
    name: build-${{ matrix.os }}
    path: dist/
    retention-days: 7

Retention defaults to 90 days for artifacts and logs (public repos; configurable, and enterprises can cap it), with per-upload retention-days allowed to shorten but never exceed the repository maximum. Administrators set defaults at the enterprise, organization, and repository level, and the objectives call out managing these via the REST APIPATCH /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention style endpoints, plus DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id} for reclaiming space immediately.

⚠️ Exam Trap: A cache miss is not an error — the workflow proceeds and simply rebuilds. If a scenario asks why a build "sometimes takes 8 minutes and sometimes 40 seconds" with no failures, that's a cache key that changes too often (for example, keyed on github.sha), not a broken cache.

Reflection Question: You key a dependency cache on ${{ github.sha }}. It technically works. Describe the two things you've destroyed — and which key you should have used instead.

See how it connects
Alvin Varughese
Written byAlvin Varughese
Founder18 professional certifications