惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
aimingoo的专栏
aimingoo的专栏
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
J
Java Code Geeks
G
Google Developers Blog
N
Netflix TechBlog - Medium
Vercel News
Vercel News
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Blog of Author Tim Ferriss
罗磊的独立博客
GbyAI
GbyAI
小众软件
小众软件
大猫的无限游戏
大猫的无限游戏
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

The Practical Developer

The Libuv Thread Pool Trap: Why Node.js Async APIs Stall Under Load Postgres Covering Indexes with INCLUDE: Eliminate Heap Fetches on Read-Heavy Workloads Postgres DISTINCT ON: The Fastest Way to Get the Latest Row Per Group Postgres Transaction Isolation: The Anomalies Your App Actually Faces in Production Linux TCP Tuning for Node.js Microservices: The Kernel Settings That Stop Silent Connection Drops Under Load Postgres HOT Updates and Fillfactor: Why Not All Writes Are Created Equal Database Connection Pool Leaks: Finding the Promise That Never Returns Its Seat Linux OOM Killer in Production: Why Your Node.js Containers Die Without a Stack Trace Postgres Materialized Views: Refresh Strategies That Do Not Lock Your Dashboards API Dependency Health Checks: Why /health Is Not Enough Authorization with Zanzibar Tuples: How Google Manages Permissions and How To Build the Same Check in Node.js Postgres Advisory Locks: The 20-Character Primitive That Replaces Redis for Coordination Dead Letter Queues: The Message Queue Pattern That Saves You at 2 a.m. File Descriptor Exhaustion: The Kernel Limit That Silently Drops Node.js Connections Graceful Degradation: The Pattern That Turns Total Outages into Partial Success PostgreSQL Full-Text Search: Dropping Elasticsearch for 90% of Use Cases S3 Presigned Multipart Uploads: Stop Your API Server from Being a File Upload Bottleneck MessagePack vs JSON: The Binary Serialization Switch That Cut Our Internal RPC Overhead by 40% DNS Caching in Node.js: The Silent Cause of Production Latency Spikes Reliable Cron Jobs: The Pattern That Stops Double Runs, Missed Executions, And The 2 AM Page GraphQL Query Complexity: Stop the OOM Query Before It Reaches Your Resolver Node.js Event Loop Lag: The Hidden Metric Behind Random Latency Spikes API Request Validation with Zod: The Schema That Catches Bad Input Before It Corrupts Your Database Load Shedding in Node.js: How to Reject Traffic Before You Drown Request Hedging: Cut Tail Latency In Half Without Overprovisioning Git Bisect: The Automated Binary Search That Finds Breaking Commits in Minutes Node.js Garbage Collection Tuning: Stop Letting V8 Pause Your Event Loop Node.js Server Timeouts: The Settings That Stop Slow Clients from Holding Sockets Hostage Postgres BRIN Indexes: The Time-Series Secret That Shrinks Indexes by 99% Event Sourcing with PostgreSQL: The Pragmatic 80% Solution
pnpm Vs npm Vs yarn Vs Bun For Monorepos: Which One Earns...
The Practica · 2024-08-30 · via The Practical Developer

The team has a 12-package monorepo on yarn 1. yarn install takes 90 seconds on cold cache, 30 seconds on warm. CI builds spend 2-3 minutes pulling dependencies. Disk usage is 4 GB across all the node_modules directories. Somebody asks if a different package manager would help.

The honest answer in 2024: yes. pnpm and Bun both install dramatically faster than npm and yarn 1. The migration is straightforward (mostly). The disk savings on a multi-package monorepo are real (often 60-70%). But the right choice depends on your stack, your CI, and how much you care about edge-case correctness.

This post is the honest comparison and the migration paths that actually work.

The four contenders

npm: the default. Bundled with Node. Has caught up significantly in npm 9+ (workspaces, faster install). Boring is a feature.

yarn 1 (Classic): the most common in legacy codebases. No new development; yarn 4 is the supported successor.

yarn 4 (Berry): major rewrite. Plug’n’Play (PnP) mode is fast but has compatibility issues; node_modules mode works like classic. Strong workspace support.

pnpm: symlink-based, content-addressed store. Saves disk, is fast, has the strictest correctness story (peer dependencies must be explicit).

Bun: package manager + runtime + bundler. Install speed is dramatic (5-10× faster than pnpm). Less mature; some npm-only edge cases break.

The numbers

A typical mid-size monorepo (12 packages, ~800 dependencies):

ToolCold installWarm installnode_modules size
npm 975s25s1.2 GB × 12 = 14 GB
yarn 160s20s1.2 GB × 12 = 14 GB
yarn 4 (nm)50s15s1.2 GB × 12 = 14 GB
pnpm25s8s1.4 GB shared
Bun8s3s1.2 GB × 12 = 14 GB

The disk number for pnpm is striking. It uses a content-addressed global store with hard links; each package is on disk once.

Why pnpm’s correctness matters

Both npm and yarn (in node_modules mode) hoist dependencies. If package A depends on lodash, and package B doesn’t but accidentally imports lodash because it’s hoisted, B works in development and breaks in production (where the hoist tree may differ).

pnpm doesn’t hoist by default. Each package’s node_modules contains only its declared dependencies. Implicit dependencies fail loudly. This is “annoying” the first week and “valuable” forever after.

// package.json declares the dependency.
{
  "dependencies": { "lodash": "^4.17.21" }
}

If you import lodash from a package that didn’t declare it, pnpm refuses. yarn / npm silently work, then fail in some other environment.

For most teams, pnpm’s strict mode is worth the migration alone; it surfaces real bugs in declared dependencies.

Bun: the speed wedge

Bun’s install speed is genuinely 5-10× faster than pnpm. For CI especially, the difference is real:

  • pnpm cold install: 25s
  • Bun cold install: 8s

For a team running 50 PRs a day with 5 CI runs each, that’s 75 minutes saved daily.

Trade-offs:

  • Compatibility. Bun’s package manager mostly works with npm-style packages. Edge cases (postinstall scripts, lifecycle hooks, native bindings) sometimes break.
  • Maturity. Newer than the alternatives. Fewer years of bug discovery.
  • Lock file. Different format (bun.lockb, binary). Can’t be merged without Bun.
  • Workspaces. Supported, but less battle-tested than pnpm’s.

For greenfield projects and hobby work, Bun is excellent. For a critical production monorepo with many dependencies, pnpm is the safer choice in 2024.

yarn 4: the modernization without migration pain

yarn 4 is yarn 1 done right. If you’re already on yarn 1 and just want a modern version:

corepack enable
yarn set version stable

Two commands, you’re on yarn 4. Most things work. PnP mode breaks more things than it fixes (until 2025+ probably); use nodeLinker: node-modules in .yarnrc.yml to stay in classic-compatible mode.

For teams that don’t want a culture shift but want to be on a maintained version, yarn 4 with node_modules linker is the path.

npm: the boring choice

npm in 2024 is faster than it was in 2020 and supports workspaces:

// root package.json
{
  "workspaces": ["packages/*"]
}
npm install
npm run build --workspace=@org/api

It doesn’t have pnpm’s strictness or Bun’s speed. It does have zero migration cost (it’s already there) and the broadest tooling compatibility. For small projects, the speed difference doesn’t matter.

For monorepos under ~5 packages, plain npm is fine. Past that, pnpm or yarn 4.

Migration paths

npm/yarn 1 → pnpm:

# Install pnpm
npm i -g pnpm

# Convert
pnpm import   # reads existing package-lock or yarn.lock, writes pnpm-lock.yaml
pnpm install

Update CI to use pnpm. Delete package-lock.json / yarn.lock. Commit pnpm-lock.yaml.

The first install is slow (cold cache). Subsequent installs use the global store and are fast.

Watch for:

  • Hoisted dependencies that become missing. Add them explicitly to the right package’s package.json.
  • Plugins / tooling that hard-coded node_modules/* paths. Most modern tooling works fine; some legacy stuff needs .npmrc shamefully-hoist-everything settings (use sparingly).
  • Patching dependencies. pnpm patch is the equivalent of patch-package.

yarn 1 → yarn 4:

corepack enable
yarn set version stable
yarn install

Add to .yarnrc.yml:

nodeLinker: node-modules    # avoid PnP if you have many dependencies
enableGlobalCache: true

Anything → Bun:

bun install

Replaces npm/yarn/pnpm install. Existing package.json works. bun.lockb is the new lock file.

For a CI-only migration without changing developer workflow:

# .github/workflows/ci.yml
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun run test

Developers stay on whatever they have; CI uses Bun. The lock file is whatever fits.

Lock file discipline

Whatever you pick:

  • Commit the lock file. Always.
  • Use npm ci / pnpm install --frozen-lockfile / yarn install --immutable in CI. Fails if the lock file doesn’t match.
  • Update the lock file deliberately (renovate, dependabot, manual), not as a side effect of npm install.

Lock files are the difference between reproducible builds and “works on my machine.”

Once you’ve picked a package manager:

  • Turborepo: build system that caches and parallelizes scripts across workspaces.
  • Nx: heavier, more features, more opinionated.
  • Lerna: older, mostly subsumed by package-manager-native workspaces.

For most monorepos, Turborepo + native workspaces is enough. Nx is the right call for very large polyglot monorepos.

The takeaway

In 2024:

  • Greenfield project, want speed: Bun.
  • Production monorepo, want correctness + speed: pnpm.
  • Already on yarn, don’t want to migrate culture: yarn 4 (node-modules linker).
  • Tiny project, don’t care: npm.
  • Legacy yarn 1: plan to migrate; pnpm is the modern default.

Pick the package manager that matches your team’s tolerance for migration effort versus daily friction. The differences are real but not life-or-death; do the migration when you have a free week, not under deadline pressure.


A note from Yojji

The kind of tooling judgment that picks the right package manager for a monorepo, and runs the migration without breaking developer flow, is the kind of detail Yojji’s teams put into the codebases they build for clients.

Yojji is an international custom software development company founded in 2016, with teams across Europe, the US, and the UK. They specialize in the JavaScript ecosystem (React, Node.js, TypeScript), cloud platforms (AWS, Azure, GCP), and full-cycle product engineering, including the build-tooling decisions that compound into daily productivity gains.