Comparisons

pnpm vs Bun Install: AI Package Management Rules

pnpm uses a content-addressable store with hard links for disk efficiency. Bun uses a global module cache with symlinks for raw speed. AI rules for lock files, workspace commands, scripts, and migration between the two.

5 min read·July 5, 2025

pnpm saves disk space. Bun saves time. The AI must know which one your project uses — every install command depends on it.

Lock files, workspace commands, script running, and the one rule that prevents every package manager conflict

Two Different Solutions to node_modules

pnpm and Bun solve the same problem differently: making JavaScript dependency management faster and more efficient. pnpm (performant npm): uses a content-addressable store at ~/.local/share/pnpm/store. Every package version is stored once on disk. Projects link to the store via hard links. Result: if 10 projects use react@19.0.0, only one copy exists on disk. Bun: uses a global module cache (~/.bun/install/cache). Packages are cached after first download. Install: copies from cache to node_modules using the fastest I/O operations available. Result: install times often under 1 second for cached dependencies.

The AI rule difference: pnpm commands use pnpm add, pnpm install, pnpm exec. Bun commands use bun add, bun install, bun run. The commands are similar but not identical. An AI generating 'npm install express' in a pnpm project: wastes the disk efficiency and creates a second package-lock.json alongside pnpm-lock.yaml. The same command in a Bun project: creates package-lock.json alongside bun.lockb. The AI must know which package manager the project uses to generate correct commands.

The practical impact: pnpm saves disk space across many projects (ideal for development machines with many repos). Bun saves time on every install (ideal for CI/CD and rapid iteration). For most teams: the choice comes down to ecosystem maturity (pnpm: 8+ years, battle-tested) vs speed priority (Bun: newer, fastest installs).

Lock Files: pnpm-lock.yaml vs bun.lockb

pnpm lock file: pnpm-lock.yaml. Human-readable YAML format. Contains: exact versions, integrity hashes, dependency resolution tree. Can be inspected and manually edited (though not recommended). Git diff: readable, shows exactly which packages changed. AI rule: 'Never regenerate pnpm-lock.yaml. Run pnpm install to update it. Commit lock file changes with dependency changes.'

Bun lock file: bun.lockb. Binary format (not human-readable). Contains: the same resolution data but in a compact binary encoding. Advantage: faster to read and write. Disadvantage: cannot be inspected in git diffs (shows as binary change). Bun also supports a text-based bun.lock (JSON) when configured. AI rule: 'bun.lockb is binary. Do not attempt to read or edit it. Run bun install to regenerate. For readable diffs: configure Bun to use bun.lock (text format).'

Migration between them: to switch from pnpm to Bun, delete node_modules and pnpm-lock.yaml, run bun install (generates bun.lockb from package.json). To switch from Bun to pnpm, delete node_modules and bun.lockb, run pnpm install (generates pnpm-lock.yaml). The AI should never mix lock files: if both pnpm-lock.yaml and bun.lockb exist, one is stale and should be removed.

⚠️ Never Mix Lock Files

If your project has both pnpm-lock.yaml and bun.lockb (or package-lock.json alongside either): one lock file is stale. The AI should detect the primary lock file and remove the other. Mixed lock files cause: different dependency versions in different environments, CI/CD failures when the wrong lock file is used, and phantom bugs from version mismatches that only appear in production.

Monorepo Workspaces: pnpm vs Bun

pnpm workspaces: configured via pnpm-workspace.yaml at the repo root. Lists workspace patterns (packages: ['packages/*', 'apps/*']). Commands: pnpm -r run build (recursive), pnpm --filter @myorg/api build (targeted). pnpm has: the most mature workspace support with filtering, topological ordering, and change detection. AI rule: 'pnpm monorepo: use pnpm --filter for targeted commands. Use pnpm -r for recursive commands. The workspace protocol (workspace:*) links local packages.'

Bun workspaces: configured via workspaces field in package.json (same as npm/Yarn). Lists workspace patterns. Commands: bun run --filter '@myorg/api' build. Bun workspaces are: functional but less mature than pnpm. Missing some advanced features like topological execution order and change-based filtering. AI rule: 'Bun monorepo: workspaces in package.json. Use bun run --filter for targeted commands. For complex monorepos with many interdependent packages: pnpm workspaces are more battle-tested.'

The workspace choice matters for AI rules because: the AI must generate correct workspace commands. pnpm --filter uses its own syntax. Bun --filter follows a different pattern. Turborepo works with both but: the underlying package manager determines how dependencies are linked, how scripts are run, and how the workspace topology is resolved.

💡 pnpm Workspace Filtering Is Powerful

pnpm --filter @myorg/api... (three dots) runs the command on @myorg/api and all its dependents. pnpm --filter '@myorg/*' targets all packages under @myorg scope. pnpm --filter '...{packages/shared}' targets everything that depends on shared. This filtering syntax: does not exist in Bun. For complex monorepos with 20+ packages, pnpm filtering saves significant CI time by building only what changed.

Running Scripts and Executables

pnpm run: pnpm run dev, pnpm run build, pnpm run test. For package binaries: pnpm exec tsx, pnpm exec vitest. The exec command: runs binaries from node_modules/.bin. pnpm dlx: downloads and runs a package without installing (like npx). AI rule: 'pnpm project: use pnpm run for scripts, pnpm exec for binaries, pnpm dlx for one-off commands. Never use npx in a pnpm project.'

Bun run: bun run dev, bun run build. For package binaries: bunx tsx, bunx vitest. Bun uses bunx instead of npx/pnpm dlx. Key difference: bun run is significantly faster than pnpm run because Bun's runtime starts faster. For TypeScript files: bun run src/script.ts runs TypeScript directly without tsx or ts-node. AI rule: 'Bun project: use bun run for scripts, bunx for one-off binaries. TypeScript files: bun run file.ts works directly (no tsx needed).'

The critical AI rule: if the project has a pnpm-lock.yaml, every command should use pnpm. If bun.lockb, every command should use bun. Mixing package managers (running npm install in a pnpm project, or npx in a Bun project) creates: duplicate lock files, inconsistent node_modules, and potential version mismatches. The AI should detect the lock file and use the correct command prefix throughout.

ℹ️ Bun Runs TypeScript Natively

In a Bun project: bun run src/seed.ts works without tsx, ts-node, or any TypeScript compilation step. Bun transpiles TypeScript internally using its JavaScriptCore engine. This means: fewer devDependencies (no tsx/ts-node), faster script execution, and simpler package.json scripts. In a pnpm project: you still need tsx or ts-node to run TypeScript files directly.

When to Use Which: Decision Framework

Choose pnpm when: you manage many projects on one machine (disk savings compound), you need mature monorepo support (pnpm workspaces + Turborepo), your CI/CD caches pnpm store effectively, your team values ecosystem stability (pnpm has been production-ready since 2017). Choose Bun when: install speed is the top priority (CI minutes cost money), you want a unified runtime + package manager (Bun runs TypeScript natively), your project is greenfield and can adopt Bun's ecosystem fully, you value the simplest possible toolchain (one binary does everything).

The AI rule summary: detect the lock file (pnpm-lock.yaml or bun.lockb) and use the corresponding commands exclusively. For new projects: both are excellent choices. pnpm: safer default (wider ecosystem compatibility). Bun: faster but may hit edge cases with packages that assume npm/Node.js conventions. The framework matters too: Next.js and Vite work with both. Remix and Astro work with both. The package manager choice is independent of the framework choice.

  • pnpm: content-addressable store, hard links, disk efficient. Best for multi-project machines
  • Bun: global cache, fastest installs, native TypeScript. Best for speed-first workflows
  • Lock files: pnpm-lock.yaml (readable YAML) vs bun.lockb (binary, fast)
  • Workspaces: pnpm-workspace.yaml (mature, filtering) vs package.json workspaces (simpler)
  • Scripts: pnpm run/exec/dlx vs bun run/bunx. Never mix package managers in one project
  • AI rule: detect the lock file, use the matching commands. pnpm-lock.yaml = pnpm. bun.lockb = bun
  • New projects: pnpm for stability. Bun for speed. Both work with all major frameworks