Architecture Decisions
ADR-0004: Bun as Package Manager
Decision to use Bun as the package manager for fast installs and modern tooling
Status
Accepted
Date: 2025-02-03
Context
Our monorepo requires a fast, reliable package manager to handle:
- Workspace dependencies between apps and packages
- Large dependency trees (Next.js, React, Radix UI, etc.)
- Frequent installs during development and CI/CD
- Lock file management for reproducible builds
- Script execution for build, dev, and test commands
Key requirements:
- Fast install times (developer productivity)
- Reliable workspace support (monorepo requirement)
- Good TypeScript tooling (we are TypeScript-first)
- Active maintenance and community
- Compatible with deployment targets
Decision
We chose Bun (v1.3.8+) as our package manager for the entire monorepo.
Why Bun?
- Speed: 10-20x faster installs than npm/yarn
- Native workspace support: First-class monorepo support
- Drop-in replacement: Uses
package.jsonand lockfile like npm - All-in-one tool: Package manager + bundler + test runner + runtime
- Excellent TypeScript support: Built for modern JS/TS workflows
Configuration:
{
"packageManager": "bun@1.3.8",
"engines": {
"node": ">=22.0.0",
"bun": ">=1.1.0"
}
}
Usage:
# Install dependencies
bun install
# Add a dependency
bun add <package>
# Run scripts
bun run dev
bun run build
# Workspaces
bun add <package> --workspace=@hn-monorepo/ui
Consequences
Positive
- Blazing fast installs: ~2-5 seconds for full install (vs 30-60s with npm)
- Faster CI/CD: Less time waiting for dependency installation
- Less disk usage: Hardlinks save ~50% disk space vs npm
- Better DX: Fast feedback loop during development
- Unified tooling: Can use Bun for testing and bundling later
- Modern by default: Optimized for ESM, TypeScript, latest standards
- Great monorepo support:
workspace:*protocol works seamlessly - Active development: Bun is rapidly improving and well-maintained
Negative
- Newer ecosystem: Less mature than npm/pnpm (released 2022)
- Occasional compatibility issues: Some packages assume npm/pnpm behavior
- Learning curve: Team must learn Bun-specific commands
- CI/CD setup: Need to install Bun in CI (not pre-installed everywhere)
- Lock file churn:
bun.lockbis binary, harder to review in PRs
Neutral
- Lock file format: Uses binary
bun.lockb(faster but not human-readable) - Node compatibility: Bun runtime is Node-compatible but not 100% identical
- Package resolution: Slightly different algorithm than npm (usually better)
Alternatives Considered
Alternative 1: npm (default)
- Description: Node.js’s built-in package manager
- Pros: Ubiquitous, no installation needed; largest community; best compatibility
- Cons: Slow install times (~30-60s); poor monorepo performance; less efficient disk usage
- Why not chosen: Too slow for our development velocity
Alternative 2: pnpm
- Description: Fast, disk-efficient package manager with great monorepo support
- Pros: Fast installs via symlinks + content-addressed storage; excellent monorepo support; strict dependency resolution
- Cons: More complex setup than npm; symlink structure can confuse some tools; slightly slower than Bun
- Why not chosen: Bun is faster and we do not need pnpm’s strictness (yet)
Alternative 3: Yarn (Classic or Berry)
- Description: Facebook’s package manager (Classic = v1, Berry = v2+)
- Pros: Yarn Classic is stable and widely used; Yarn Berry has fast, zero-installs with PnP
- Cons: Yarn Classic is slow and deprecated; Yarn Berry is complex with a steep learning curve and PnP breaks many tools
- Why not chosen: Bun is faster and simpler than Berry, more modern than Classic
References
Notes
- Installation: Developers must install Bun locally (
curl -fsSL https://bun.sh/install | bash) - CI/CD: GitHub Actions use
oven-sh/setup-bun@v1to install Bun - Compatibility: If a package does not work with Bun, we can fall back to npm for that specific command
- Lock file: Committed
bun.lockbensures reproducible builds across team and CI - Rollback plan: If Bun causes issues, we can switch to pnpm with minimal changes (both support
workspace:*protocol)