Pick your runtime, lock the version
Node has three common runtimes in active use: Node.js (Classic), Deno, Bun. For most production apps it's still Node.js. Package managers: npm (default), pnpm (fast, content-addressed), yarn (still around), bun (blazing fast, also a runtime).
The default Node CI shape:
- Checkout.
actions/setup-node@v4with a pinned version and cache for the package manager.npm ci(orpnpm install --frozen-lockfile,yarn install --immutable,bun install) — clean, reproducible install from the lockfile.- Lint:
eslintorbiome. - Type check:
tsc --noEmit. - Test:
vitest runorjestornode --test.
Common pitfalls
npm installin CI is wrong — it can update the lockfile silently. Usenpm ci.- Default cache key for
actions/setup-nodeis the lockfile hash. If you have a monorepo with multiple lockfiles, setcache-dependency-path:explicitly. - Don't run a separate
npm installper matrix cell — share the install across the matrix unless versions differ.