🎯 JavaScript/Node Package Managers — The Complete, Hands‑On Guide (npm · Yarn · pnpm · Bun) Print

  • 0

🧭 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.

  • Scriptsnpm run, yarn, pnpm expose 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_modules layout, workspaces OK.

  • Good for maintaining existing repos without big changes.

Yarn Berry (v2+)

  • PnP: virtual filesystem; optionally switch to node-modules linker.

  • Zero‑install: commit .yarn/cache to 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_modules saves 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 install is 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, use yarn install --immutable.
pnpm: install via Corepack and use pnpm-lock.yaml.


🔁 10) Migration playbooks

Yarn v1 → npm

  1. Replace yarn <script> with npm run <script>.

  2. Convert resolutions → npm overrides.

  3. Generate fresh package-lock.json with npm ci.

npm → pnpm

  1. Add pnpm-workspace.yaml.

  2. Fix peer‑dep warnings; avoid assuming flat hoist.

  3. Use pnpm install --frozen-lockfile in CI.

Yarn v1 → Yarn Berry

  1. Add .yarnrc.yml + choose pnp or node-modules linker.

  2. (Optional) Enable zero‑install by committing .yarn/cache.

  3. Patch tools that expect node_modules or 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_modules across OSes.

  • Tool mismatch → Enforce via packageManager + a preinstall guard.

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-lockfile installs.

  • 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. 🚀


Was this answer helpful?

« Back