🧭 TL;DR Summary
-
npm → best default: simple, built‑in, reliable workspaces.
-
pnpm → fastest + most space‑efficient for big monorepos; strict linking.
-
Yarn Berry → PnP (no
node_modules) + zero‑install; powerful but opinionated. -
Yarn v1 → legacy, fine to maintain, but new features are in Berry.
-
Bun → very fast, integrated runtime/tester; ecosystem still maturing.
✅ Commit one lockfile. ✅ Pin Node & tool versions. ✅ Use workspaces. ✅ Enforce overrides/resolutions. ✅ Use ci/immutable installs in CI.
🧩 1) What does a package manager do?
-
Resolves dependency versions from
package.json(incl. transitive deps). -
Writes a lockfile for reproducible installs.
-
Installs packages into
node_modules(or a virtual FS in Yarn PnP). -
Exposes project CLIs via
node_modules/.bin/. -
Supports workspaces/monorepos.
-
Talks to an npm registry (public/private) with auth, caching, proxies.
🧪 2) Options at a glance
| Manager | Lockfile | Install model | Workspaces | Superpowers | Trade‑offs |
|---|---|---|---|---|---|
| npm | package-lock.json |
Classic node_modules |
Yes (v7+) | Ubiquitous, easy, 1st‑party, good defaults | Not the absolute fastest; fewer monorepo niceties than pnpm |
| Yarn v1 (Classic) | yarn.lock |
Classic node_modules |
Yes | Battle‑tested; common in older monorepos | Legacy; new work focuses on Berry |
| Yarn Berry (v2+) | yarn.lock |
PnP (or node‑modules via plugin) | Yes | Zero‑install, constraints, plugins, very fast | PnP may break tools that expect node_modules; learning curve |
| pnpm | pnpm-lock.yaml |
Linked node_modules from content‑addressable store |
Yes | Disk‑efficient, fast, strict, great filters | Some tools assume flat hoisting; needs tweaks |
| Bun | bun.lockb |
Classic node_modules |
Basic | Blazing fast + runtime/test | Binary lockfile; ecosystem still maturing |
🧰 Manager‑of‑managers: Corepack (bundled with Node ≥16.17) pins Yarn/pnpm versions via packageManager in package.json.
🧠 3) Core concepts (that apply to all)
-
Lockfile → Deterministic graph; must be committed.
-
Hoisting → Flatten deps to top‑level; convenient but can mask duplication.
-
Peer deps → Required by consumers; misaligned peers cause install errors.
-
Overrides/Resolutions → Force versions of nested deps (npm/pnpm:
overrides, Yarn:resolutions). -
Workspaces → Single repo, many packages, one lockfile; local linking.
-
Scripts →
npm run,yarn,pnpmexpose CLIs from.bin. -
Corepack → Declares & provisions Yarn/pnpm versions per project.
🔵 4) npm — the reliable default
Why npm?
-
Ships with Node, minimal friction, strong CI/Docker ergonomics.
-
Modern npm supports workspaces, peer‑dep auto‑install,
overrides.
Common commands
npm i # install
npm ci # clean, lockfile‑only install for CI
npm run -ws build # run script in all workspaces
npm run build -w pkg # run in one workspace
npm outdated && npm audit
Helpful snippets
// package.json (excerpt)
{
"private": true,
"workspaces": ["apps/*", "packages/*"],
"engines": { "node": ">=20 <21" },
"packageManager": "npm@10"
}
# .npmrc
engine-strict=true
fund=false
audit=true
// npm overrides
{
"overrides": {
"ansi-regex": "6.0.1",
"react-dom@^18": "18.3.1"
}
}
🟣 5) Yarn — Classic vs Berry
Yarn v1 (Classic)
-
Familiar
node_moduleslayout, workspaces OK. -
Good for maintaining existing repos without big changes.
Yarn Berry (v2+)
-
PnP: virtual filesystem; optionally switch to
node-moduleslinker. -
Zero‑install: commit
.yarn/cacheto avoid network in CI. -
Plugins: Constraints, version policies, interactive upgrades.
Berry basics
# .yarnrc.yml
nodeLinker: pnp # or 'node-modules'
yarnPath: .yarn/releases/yarn-4.x.cjs
npmRegistryServer: https://registry.npmjs.org/
// package.json
{ "resolutions": { "left-pad": "1.3.0" } }
yarn install --immutable
# Run in all workspaces
yarn workspaces foreach -A run build
🟢 6) pnpm — fast & space‑efficient
Why pnpm?
-
Content‑addressable store + symlinked
node_modulessaves disk. -
Strict linking catches phantom deps early.
-
Excellent monorepo filters & commands.
Workspace setup
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
# .npmrc (pnpm respects many npmrc keys)
shamefully-hoist=false
strict-peer-dependencies=true
pnpm install
pnpm -r build # recursive in all packages
pnpm -F @scope/pkg test # filter target
pnpm prune --prod
🟠 7) Bun — speed + all‑in‑one runtime
-
bun installis very fast; lockfile is binary (bun.lockb). -
Bundled test runner & runtime — great for prototypes/greenfield.
-
Ensure ecosystem tools (plugins/CLIs) behave as expected.
bun install
bun run build
🧱 8) CI/CD recipes (copy‑paste friendly)
GitHub Actions — npm
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npm run -ws build --if-present
- run: npm test --workspaces --if-present
GitHub Actions — pnpm
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'pnpm' }
- run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm -r build
GitHub Actions — Yarn Berry
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: corepack enable
- run: yarn install --immutable
- run: yarn workspaces foreach -A run build
🐳 9) Docker patterns (prod‑ready)
Builder → Runtime (npm)
FROM node:20 AS deps
WORKDIR /app
COPY package.json package-lock.json ./
COPY packages/*/package.json apps/*/package.json ./
RUN npm ci --include-workspace-root
FROM node:20 AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run -ws build
FROM node:20-slim
WORKDIR /app
COPY --from=build /app .
RUN npm prune --omit=dev
CMD ["node","apps/api/dist/main.js"]
Yarn Berry: copy
.yarn+yarn.lock, useyarn install --immutable.
pnpm: install via Corepack and usepnpm-lock.yaml.
🔁 10) Migration playbooks
Yarn v1 → npm
-
Replace
yarn <script>withnpm run <script>. -
Convert
resolutions→ npmoverrides. -
Generate fresh
package-lock.jsonwithnpm ci.
npm → pnpm
-
Add
pnpm-workspace.yaml. -
Fix peer‑dep warnings; avoid assuming flat hoist.
-
Use
pnpm install --frozen-lockfilein CI.
Yarn v1 → Yarn Berry
-
Add
.yarnrc.yml+ choosepnpornode-moduleslinker. -
(Optional) Enable zero‑install by committing
.yarn/cache. -
Patch tools that expect
node_modulesor switch to node‑modules linker.
🛠️ 11) Troubleshooting cookbook
-
Peer dependency conflict → Align at the top level or use overrides/resolutions.
-
Lockfile merge hell → Re‑install from clean tree; avoid manual edits; re‑generate.
-
EAI_AGAIN / network flakiness → Use registry mirrors/caching; Yarn zero‑install; pnpm store.
-
Native addon fails on new OS/arch → Reinstall on target OS; don’t copy
node_modulesacross OSes. -
Tool mismatch → Enforce via
packageManager+ apreinstallguard.
Preinstall guard (enforce npm only)
{
"scripts": {
"preinstall": "node -e \"const ua=process.env.npm_config_user_agent||''; if(!ua.startsWith('npm/')){console.error('Use npm only.'); process.exit(1)}\""
}
}
🔐 12) Security & compliance
-
Use scoped tokens in CI; never commit secrets.
-
Enable 2FA on registry accounts.
-
Regularly audit (
npm audit,pnpm audit,yarn npm audit). -
Pin critical transitive deps via overrides/resolutions.
-
Mirror or proxy registry (Verdaccio/Artifactory) for availability & provenance.
❓ 13) FAQ (quick hits)
-
Should I commit
node_modules? → ❌ No. Commit only the lockfile. -
One repo, many packages? → ✅ Use workspaces (npm/pnpm/Yarn all support).
-
Which is fastest? → pnpm/Yarn Berry are typically faster; Bun is very fast too.
-
Which is safest for a broad team? → npm or pnpm.
-
Can I mix managers? → Avoid mixing; enforce a single tool per repo.
📚 14) Glossary
-
Lockfile: Snapshot of exact versions for reproducibility.
-
Hoisting: Lifting nested deps to top‑level
node_modules. -
Peer dependency: A dep you must install at the app level for a package to integrate correctly.
-
PnP: Plug‑and‑Play; Yarn’s virtual filesystem that replaces
node_modules. -
Corepack: Node helper that provisions Yarn/pnpm per project.
✅ 15) One‑page team checklist
-
Choose: npm / pnpm / Yarn Berry (document why).
-
Pin Node & tool via
engines+packageManager. -
Commit one lockfile at repo root.
-
Use workspaces with a single graph.
-
CI uses
ci/immutable/--frozen-lockfileinstalls. -
Use overrides/resolutions (never patch inside
node_modules). -
Regular audits; track advisories.
-
Production images prune dev deps.
-
Document commands for devs & CI.
🏁 Final take
For most teams: start with npm for simplicity, or pnpm for efficiency at scale. If you want PnP/zero‑install and can accommodate it, Yarn Berry is excellent. Whatever you choose, lock it down (versions + lockfile), automate CI, and keep the graph healthy with audits and overrides. 🚀