home / skills / phrazzld / claude-config / bun-best-practices

bun-best-practices skill

/skills/bun-best-practices

This skill helps you decide when to use Bun for package management or runtime, and guides migration, workspace configuration, and anti-patterns.

npx playbooks add skill phrazzld/claude-config --skill bun-best-practices

Review the files below or copy the command above to add this skill to your agents.

Files (5)
SKILL.md
6.4 KB
---
name: bun-best-practices
description: |
  When to use Bun, patterns, and anti-patterns. Covers Bun as package manager
  vs runtime, workspace configuration, compatibility considerations.
  Invoke for: bun adoption decision, bun vs pnpm, bun migration planning.
user-invocable: false
effort: high
---

# Bun Best Practices

Foundational knowledge for Bun adoption decisions. Bun serves two distinct roles—understand when each applies.

## Two Distinct Roles

### Bun as Package Manager

Replaces pnpm/npm/yarn for dependency management:
- `bun install` — Install dependencies (fast, binary lockfile)
- `bun add <pkg>` — Add dependency
- `bun remove <pkg>` — Remove dependency
- `bun --filter <workspace>` — Run in specific workspace

**Key differences from pnpm:**
- Lockfile: `bun.lock` (binary) vs `pnpm-lock.yaml` (YAML)
- Workspaces: Defined in root `package.json`, no separate `pnpm-workspace.yaml`
- Speed: Significantly faster installs due to binary lockfile and caching

### Bun as Runtime

Replaces Node.js for executing JavaScript/TypeScript:
- `bun run script.ts` — Execute TypeScript directly (no tsc)
- `bun test` — Built-in test runner (Jest-compatible API)
- `bun build` — Bundle for production
- Native SQLite, file I/O, HTTP server optimizations

**Key differences from Node.js:**
- TypeScript: Transpilation, not full tsc checking (use `tsc --noEmit` for types)
- APIs: Some Node.js APIs missing or behave differently
- Performance: Often 2-10x faster for I/O-heavy workloads

## Decision Tree: When to Use Bun

```
┌─────────────────────────────────────────────────────────┐
│                   Consider Bun When:                    │
├─────────────────────────────────────────────────────────┤
│ ✅ CLI tools (fast startup, no cold boot)               │
│ ✅ Edge functions (lightweight runtime)                 │
│ ✅ Internal dev tools (team controls deployment)        │
│ ✅ New greenfield projects (no legacy constraints)      │
│ ✅ Scripts/automation (fast execution)                  │
│ ✅ Projects with simple dependency trees                │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                   Prefer pnpm When:                     │
├─────────────────────────────────────────────────────────┤
│ ⚠️ Expo/EAS builds (Node.js required)                  │
│ ⚠️ Vercel serverless (limited Bun support)             │
│ ⚠️ Complex native module dependencies                   │
│ ⚠️ Team unfamiliar with Bun quirks                     │
│ ⚠️ Production apps needing maximum stability           │
│ ⚠️ Projects with extensive Node.js API usage           │
└─────────────────────────────────────────────────────────┘
```

## Hybrid Setup Pattern

Many projects benefit from using both:

```
monorepo/
├── apps/
│   ├── web/          # Next.js app → pnpm (Vercel deployment)
│   └── mobile/       # Expo app → pnpm (EAS requires Node)
├── packages/
│   └── shared/       # Shared utilities → either works
├── tools/
│   └── cli/          # Internal CLI → Bun (fast startup)
└── scripts/          # Build/deploy scripts → Bun (fast execution)
```

**Rule of thumb:**
- External-facing production apps → pnpm (stability)
- Internal tools and scripts → Bun (speed)

## Workspace Configuration

### pnpm (separate file)
```yaml
# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
```

### Bun (in package.json)
```json
{
  "workspaces": ["apps/*", "packages/*"]
}
```

**Migration note:** Remove `pnpm-workspace.yaml` when switching to Bun; add `workspaces` to root `package.json`.

## Anti-Patterns

### 1. Assuming Full Node.js Compatibility

**Wrong:**
```typescript
// Assuming all Node.js APIs work identically
import { fork } from 'child_process';
fork('./worker.js'); // May behave differently in Bun
```

**Right:**
```typescript
// Test critical Node.js API usage before migration
// Check: https://bun.sh/docs/runtime/nodejs-apis
```

### 2. Mixing Lockfiles

**Wrong:**
```
project/
├── bun.lock
├── pnpm-lock.yaml    # Both present = confusion
└── package.json
```

**Right:**
```
project/
├── bun.lock          # OR pnpm-lock.yaml, not both
└── package.json
```

### 3. Skipping CI Migration

**Wrong:**
```yaml
# Still using pnpm in CI
- uses: pnpm/action-setup@v4
- run: pnpm install
```

**Right:**
```yaml
# Match local tooling
- uses: oven-sh/setup-bun@v2
- run: bun install
```

### 4. Ignoring Platform Support

**Wrong:**
```typescript
// Deploying to platform that doesn't support Bun runtime
export default async function handler(req, res) {
  // Assumes Bun runtime features
}
```

**Right:**
```typescript
// Verify deployment target supports Bun
// Vercel: experimental Bun runtime flag
// Netlify: Node.js only (as of 2025)
// Fly.io: Full Bun support
```

## Performance Benchmarking

Before migrating, benchmark your specific workflows:

```bash
# Install time comparison
time pnpm install
time bun install

# Script execution comparison
time pnpm run build
time bun run build

# Test runner comparison
time pnpm test
time bun test
```

Only migrate if Bun provides measurable benefit for YOUR project.

## Related References

- `references/workspace-config.md` — Workspace migration details
- `references/compatibility-matrix.md` — Framework/tool compatibility
- `references/ci-cd-patterns.md` — CI/CD configuration patterns
- `references/hybrid-setup.md` — Running Bun + pnpm together

## Related Skills

- `/check-bun` — Audit project for Bun compatibility
- `/fix-bun` — Fix Bun migration issues
- `/bun` — Full Bun migration orchestrator

Overview

This skill explains when to adopt Bun, how to use it as a package manager vs a runtime, and practical migration patterns and anti-patterns. It helps teams choose Bun vs pnpm, design hybrid monorepos, and avoid common pitfalls during migration. Use it to make informed Bun adoption decisions and plan safe rollouts.

How this skill works

The skill inspects Bun’s dual roles: fast dependency management and a high-performance JavaScript/TypeScript runtime. It outlines workspace configuration differences, compatibility gotchas with Node.js APIs, and recommended CI changes. It also presents decision criteria, hybrid patterns for mixed toolchains, and simple benchmarking steps to validate benefits.

When to use it

  • When building internal CLIs, scripts, or dev tools that benefit from fast startup.
  • For edge functions or lightweight serverless workloads with simple dependency trees.
  • When starting greenfield projects where migration cost is low.
  • For performance-sensitive I/O workloads where Bun’s runtime shows measurable gains.
  • When you want faster installs and developer iteration speed for non-production-facing tooling.

Best practices

  • Treat Bun and pnpm as complementary: use pnpm for externally facing production apps and Bun for internal tools and scripts.
  • Define workspaces in the root package.json for Bun; remove pnpm-workspace.yaml if fully switching.
  • Benchmark install, build, and test workflows before migrating critical services.
  • Do not assume full Node.js API parity—test Node-specific code paths and native modules early.
  • Keep a single lockfile per project (bun.lock OR pnpm-lock.yaml) to avoid confusion.

Example use cases

  • Monorepo that uses pnpm for Next.js and Expo apps, and Bun for internal CLI and deployment scripts.
  • Migrating developer tooling to Bun to dramatically reduce install and script execution times.
  • Adopting Bun for edge function runtimes and small serverless handlers with simple dependencies.
  • Using hybrid setup: pnpm for production web/mobile apps; Bun for fast-running automation and CI steps.

FAQ

Can I run Bun and pnpm in the same repository?

Yes. Use a hybrid pattern: keep pnpm for production-facing apps and use Bun for tools or scripts. Define workspaces consistently and avoid mixing lockfiles for the same packages.

Does Bun fully replace tsc and Node.js type checking?

No. Bun transpiles TypeScript but does not perform full type checking. Run tsc --noEmit or a separate type-check step in CI to ensure type safety.

What CI changes are needed when adopting Bun?

Match local tooling in CI: replace pnpm setup and install steps with oven-sh/setup-bun and bun install when migrating those jobs. Keep pnpm in CI for jobs that must run under Node.js.