<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

npm, yarn or pnpm: When to choose which Package Manager

The choice of package manager is not a stylistic detail: it directly impacts build in times CI, the disk space on each development machine and runner, the attack surface for dependencies malicious, and the convenience of managing a monorepo with dozens of interdependent packages. npm, Yarn e pnpm solve the same problem with radically different node_modules architectures, and the wrong choice for your context pays off in wasted CI minutes every single build, not in a one-off problem.

Quick Overview

CriterionnpmYarn (Berry/v4)pnpm
Installation speed (hot cache)GoodExcellentExcellent
Lockfilepackage-lock.jsonyarn.lockpnpm-lock.yaml
Workspace supportNative, essentialNative, matureNative, most advanced for monorepo
Dedup dependenciesPartialGoodExcellent (centralized store)
Disk usageHigh (multiple copies)Medium/low (PnP avoids node_modules)Very low (hard link from store global)
Network cachingBasicGoodExcellent
Security/auditnpm integrated audityarn npm auditpnpm audit, strict layout prevents phantom dependency
Ecosystem/compatibilityMaximum (default Node.js)High, PnP can break legacy toolsHigh, strict layout can break code with import implicit

Always check the installed versions before deciding: npm -v, yarn -v, pnpm -v, and for npm in particular npm view npm versions to find out the latest releases available.

Criteria for Selection by Context

Small / Single Dev Project

npm is the correct default choice: zero additional configuration (comes with Node.js), maximum compatibility with every tool and tutorial, no practical benefit from a dedup advanced on a project with a few dozen dependencies.

Medium Team (3-10 Dev)

pnpm starts paying off the setup investment: faster installs on shared CI, less disk space on each team laptop, and the strict layout prevents sneaky bugs from phantom dependency (import of packages not explicitly declared that "just happen to work" with npm/Yarn classic).

Monorepo Enterprise (Dozens of Packages)

pnpm workspaces is the de facto standard for large monorepos: dedup aggressive via centralized store, powerful filters (pnpm --filter) to run commands only on packages actually modified, and install times that remain manageable too with hundreds of interdependent packages.

Library Publishable on npm Registry

The development package manager is independent from the consumer package manager: any of three works for publishing, but pnpm helps to discover missing peerDependencies first of the publication thanks to its strict layout, which does not "hide" transitive dependencies such as accessed by mistake.

CI/CD and Cloud Caching

For GitHub Actions/GitLab CI/Vercel pipelines, pnpm and Yarn Berry offer more effective caching thanks to global store/cache reusable between runs, while npm requires the entire cache node_modules (heavier to save/restore with each build).

Technical Details for Package Manager

npm

npm install                  # installa da package.json, aggiorna il lockfile se necessario
npm ci                       # installa esattamente da package-lock.json, mai lo modifica — usa questo in CI
npm audit                    # scansione vulnerabilità note
npm outdated                 # dipendenze con versioni più recenti disponibili

npm ci (not npm install) should always be used in CI: it explicitly fails if package.json and package-lock.json are misaligned, instead of silently update the lockfile during a build.

Yarn (Berry / v4)

yarn set version berry        # migra al Yarn moderno (Plug'n'Play di default)
yarn install                  # installa dipendenze
yarn workspaces foreach run build   # esegue uno script su ogni workspace del monorepo
yarn npm audit                # audit di sicurezza

Plug'n'Play (PnP) eliminates node_modules in favor of a single file .pnp.cjs which maps resolutions — install almost instant, but some legacy tools that assume the physical existence of node_modules may break and require fallback to nodeLinker: node-modules. Zero-install commit the compressed cache of dependencies in the repository itself, completely eliminating the time of installing in CI at the cost of a heavier repository.

pnpm

pnpm install                  # installa usando lo store centralizzato con hard link
pnpm import                   # converte un package-lock.json/yarn.lock esistente in pnpm-lock.yaml
pnpm -w add typescript -D     # aggiunge una dipendenza alla root del workspace
pnpm store path                # mostra il percorso dello store centralizzato condiviso

pnpm's strict layout (each package only sees its own declared dependencies, not the entire flattened graph) is the most important architectural difference: it prevents phantom dependency, but it can break existing code that implicitly depended on transitive dependencies never declared — a problem that is only discovered at the first pnpm install on a project migrated, it should therefore be anticipated with a code audit before migration.

Practical Benchmarks

#!/bin/bash
# benchmark-install.sh — confronta tempi di install a cache fredda/calda
for pm in npm yarn pnpm; do
  rm -rf node_modules
  echo "=== $pm (cache fredda) ==="
  time $pm install --force 2>&1 | tail -1
  echo "=== $pm (cache calda) ==="
  rm -rf node_modules
  time $pm install 2>&1 | tail -1
done
# Misura uso disco: node_modules locale vs store centralizzato pnpm
du -sh node_modules
pnpm store path && du -sh "$(pnpm store path)"

To reproduce reliable benchmarks in CI, always run at least 3 consecutive runs discarding the first one (filesystem/runner cache effects), and fixes the package manager version in the workflow — a comparing different versions of npm/Yarn/pnpm invalidates any conclusions about the differences real architectural.

Case Study 1: Small App (1-3 Developers)

An internal app with 3 developers and ~60 direct dependencies. Recommended: npm, because zero additional setup beats any marginal performance gain on a project this scale. Onboarding a new developer: under 5 minutes (npm install and go), against potential initial friction in explaining PnP or the centralized store for a benefit almost nothing at this scale.

# GitHub Actions — small app, npm con cache standard
- uses: actions/setup-node@v4
  with: { node-version: 22, cache: 'npm' }
- run: npm ci
- run: npm run build

Case Study 2: Monorepo Enterprise (20+ Packages)

A monorepo with 24 packages (8 applications, 16 shared libraries) and team distributed across 3 time zones times. Recommended: pnpm workspaces. Structure:

// package.json root
{ "workspaces": ["apps/*", "packages/*"], "packageManager": "pnpm@9.0.0" }
# GitHub Actions — caching dello store pnpm condiviso tra run
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
  with: { node-version: 22, cache: 'pnpm' }
- run: pnpm install --frozen-lockfile
- run: pnpm -w run build --filter=...[origin/main]

Estimated migration effort from npm workspaces to pnpm: ~60 person-hours for a monorepo of this size (phantom dependency audit included). Expected result: CI install time reduced by 40-60%, disk space on the runners reduced proportionally to the number of packets sharing the same dependencies.

Case Study 3: Public Library on npm Registry

An open source library with peerDependencies on Angular/React. Recommended choice: pnpm in development (to discover missing peer dependencies thanks to strict layout), publication still compatible with any consumer-side package manager.

{
  "peerDependencies": { "react": "^18.0.0 || ^19.0.0" },
  "peerDependenciesMeta": { "react": { "optional": false } }
}
# Publish workflow
npm publish --dry-run          # verifica cosa verrebbe pubblicato prima del publish reale
pnpm publish --access public   # publish reale (funziona identicamente per consumer npm/yarn/pnpm)

Migration and 30/60/90 Day Operational Plan

Days 1-30: Evaluation and Pilot

  • Pre-migration checklist: backup of existing lockfile, test coverage baseline, snapshot of current CI pipeline.
  • Pilot migration on a single non-critical package/repo — KPI: Post-migration green build and testing.
# Migrazione da npm/yarn a pnpm preservando le versioni risolte nel lockfile esistente
pnpm import
pnpm install

Days 31-60: Progressive Rollout

  • Migrate remaining packages one at a time, never all at once — KPI: 0 functional regressions per migrated package.
  • Updating CI pipeline with new package manager native caching — KPI: CI build time measured before/after.

Days 61-90: Consolidation

  • Removing legacy lockfiles and old package manager from documentation — KPI: 0 residual references to previous tool.
  • Final security and disk usage audit — KPI: reduction measured on both metrics.

Rollback plan: Keep the previously committed package manager lockfile until finished confirmed migration; going back simply means restoring that lockfile and the original install command, without any other code changes.

CI/CD and Caching

# GitLab CI — cache dello store pnpm tra pipeline
cache:
  key: pnpm-store
  paths:
    - .pnpm-store
variables:
  PNPM_STORE_PATH: .pnpm-store

For Yarn Berry, cache the .yarn/cache folder (not node_modules, which with PnP may not exist at all); for npm, cachea ~/.npm plus possibly node_modules if the project is small enough to make recovery faster than an install from scratch.

Security and Policy

npm audit --audit-level=high
yarn npm audit
pnpm audit

pnpm's strict layout offers an indirect but real security benefit: a package cannot accidentally access a compromised transitive dependency that you didn't explicitly declare, reducing the attack surface of supply chains compared to flat node_modules traditional npm/Yarn classic. In any case, integrate the package manager audit into CI as a step blocker, not as an occasional manual check, and consider a dedicated SCA tool (Snyk o equivalent) for projects with more stringent compliance requirements.

FAQ

Can I mix different package managers in the same project?

Technically possible but strongly discouraged: multiple lockfiles and different resolution behaviors cause inconsistencies that are difficult to diagnose.

pnpm always breaks existing projects?

No, but the strict layout can reveal pre-existing phantom dependencies: a code audit before migration reduces the risk of surprises.

Is Yarn PnP compatible with all tools?

No, some tools that assume physical node_modules on disk require fallback to nodeLinker: node-modules.

Do you need npm ci or just npm install in CI?

npm ci is always preferable in CI: it is faster and fails explicitly if the lockfile is misaligned, rather than silently modifying it.

How do you handle lockfile conflicts in a merge?

Regenerate the lockfile from the updated branch instead of manually resolving conflicts line by line, which is almost always slower and riskier.

Is pnpm more secure than npm?

Provides an indirect benefit via the strict layout that limits access to undeclared transitive dependencies, but auditing of known vulnerabilities remains necessary with all three.

How do you handle missing peer dependencies?

pnpm explicitly reports them during installation thanks to the strict layout; with classic npm/Yarn they must be verified manually or with dedicated tools.

Does

pnpm work well on Windows?

Yes, but hard links require the store and project to be on the same volume/disk to work optimally.

Is it worth changing package manager halfway through an already started project?

Only if the expected benefits (CI, disk, monorepo) clearly outweigh the cost of migration and testing; for a small stable project it is often not convenient.

How do you choose between Yarn and pnpm for a new monorepo?

Both are valid; pnpm generally has the most aggressive dedup and most mature filters for large monorepos, Yarn PnP eliminates node_modules entirely if tool compatibility allows.

Common Errors and Quick Fixes

  • Using npm install instead of npm ci in CI: risks silently modifying the lockfile during a build — always use npm ci.
  • Commit node_modules to repository: bloat repository unnecessarily, use .gitignore and rely on lockfile for reproducibility.
  • Mix lockfiles from different package managers: Always remove other lockfiles when adopting a new one.
  • Ignore phantom dependency warnings after a migration to pnpm: they are real latent bugs, not false positives to be silenced.
  • Do not fix the package manager version in the project: Use the packageManager field in package.json to ensure consistency between developers and CIs.
  • CI cache not invalidated after lockfile change: cache key must include the lockfile hash, not be static.
  • Yarn PnP with incompatible legacy tools: Check compatibility before adopting PnP on a project with dependencies on legacy tools.
  • Store pnpm on disk/volume different from the project: breaks the effectiveness of hard links on Windows, checks the configuration of the store path.
  • Security audit performed manually only: Integrate this as a blocking CI step, not a one-time check.
  • Migrate an entire monorepo in one go: drastically increases risk, migrate package by package with verification at every step.

Final Checklist: Rapid Decision Matrix

CriterionRecommended Choice
Small team (1-3 devs)npm
Monorepo with dozens of packagespnpm
Tightening disk space constraintspnpm (or Yarn PnP)
Fast CI as top prioritypnpm or Yarn Berry with caching store
Phantom dependency security/preventionpnpm (strict layout)
Public library with maximum consumer compatibilityIndifferent publish side, pnpm in development

How to Check

  • Replay the install benchmarks with the provided script on hardware/IC representative of your real case.
  • Verify that the cache is actually reused in CI (check workflow cache hit/miss logs).
  • Run the full E2E suite after every package manager migration, not just unit tests.
  • Check the integrity of the lockfile: npm ci/pnpm install --frozen-lockfile should complete unchanged.
  • Perform a publish dry-run before each real publish: npm publish --dry-run.
  • Monitor disk usage over time on CI and development machines, not just at migration time.

Conclusion

There is no universally better package manager: npm remains the lowest friction choice for small projects, pnpm offers the most concrete benefits on monorepo and high build frequency CI, Yarn Berry with PnP is a valid option when the compatibility of the tools allows it and you want to eliminate it node_modules entirely. The correct decision depends on the size of the team, the repository structure and actual CI and disk constraints — not a generic preference based on popularity.

Do you want the complete benchmark script for your project or a migration evaluation best suited to your team? Request a technical audit: in a few hours of analysis it is possible Estimate the real benefits and risks of a package manager migration for your codebase.

💬 Reader notes

0 notes

Write a note

Share your opinion, a suggestion or a compliment

Latest notes

No notes yet. Be the first to comment!