npm, Yarn, or pnpm: The Disk and Speed Tradeoffs That Actually Matter
Phantom dependencies are a time bomb in any npm or Yarn v1 project: code silently relies on packages that could vanish with the next minor upgrade. pnpm makes that class of bug impossible and cuts CI install times enough to matter on paid runner minutes.
The three major Node.js package managers differ less in their CLI commands than in how they store dependencies on disk — a choice that cascades into install speed, CI costs, and a whole class of subtle runtime bugs. npm and Yarn v1 both flatten node_modules, which hoists transitive dependencies to the top level and lets code import packages never declared in package.json. Those phantom dependencies work until an upstream package drops them, at which point the build breaks with no obvious cause.
pnpm replaces the flat node_modules model with a global content-addressable store and hard links. Each package version lives on disk exactly once, regardless of how many projects use it. More importantly, pnpm enforces strict dependency boundaries: a package can only access what it explicitly declares, so phantom imports fail at install time rather than surfacing as production errors months later.
Benchmarks on a ~200-dependency project show pnpm cutting cold-install times nearly in half versus npm, and repeated cached installs dropping to 3 seconds. In monorepos, pnpm workspaces share a single copy of each dependency across all packages, and its `--filter` flag runs only the packages affected by a change — a direct reduction in CI minutes and disk bills.
The phantom dependency problem is more dangerous than most teams realize: it doesn't just break builds, it can silently change runtime behavior when a transitive dependency bumps a patch version, with no error and no obvious place to look.
pnpm's hard-link model is the only one of the three that makes monorepo dependency sharing a physical reality rather than a hoisting heuristic — the disk savings are real, not just logical deduplication.
Yarn Berry's PnP is technically the most radical and correct approach, but its ecosystem incompatibility has made it a non-starter for most teams, illustrating that package management is as much a social coordination problem as a technical one.
The migration cost from npm or Yarn to pnpm is dominated by phantom dependency cleanup, which is actually a latent quality audit that would benefit the project regardless of the package manager switch.