ADR-001: Bun as the Application Runtime

Status: Accepted

Context

A modern full-stack TypeScript project needs a runtime, package manager, bundler, and test runner. Historically these were separate tools: Node.js (runtime) + npm/yarn/pnpm (packages) + webpack/Vite (bundler) + Jest/Vitest (tests). Each tool adds configuration overhead, version-pinning friction, and its own cold-start cost.

The key requirements were:

  • Native TypeScript execution without a compile step in development
  • Fast package installs and a single lockfile
  • A built-in bundler that can produce a production frontend bundle
  • A test runner that runs in the same runtime as the application code
  • Automatic .env loading without dotenv
  • Low overhead for a boilerplate that others will clone and customise

Decision

Use Bun as the sole runtime, package manager, bundler, and test runner across the entire stack — both backend and frontend.

ConcernToolReplaces
RuntimeBun.serve()Node.js + Express/Fastify
Package managerbun installnpm / yarn / pnpm
BundlerBun bundlerVite / webpack / esbuild
Test runnerbun:testJest / Vitest
Env loadingBun.env (built-in)dotenv
File I/OBun.file()fs.readFile / fs.writeFile
Shell commandsBun.$\cmd“execa / child_process

Specific Bun capabilities used in this codebase:

  • Bun.serve() — the HTTP + WebSocket server; routes are declared as a plain object, no framework overhead
  • Bun.password.hash() / Bun.password.verify() — native argon2 / bcrypt, no native addon compilation
  • HMR in devimport.meta.hot for frontend hot module reloading without Vite
  • --hot flagbun --hot backend/server.ts for backend hot reload in development
  • bun:test with happy-dom — frontend and backend unit tests in a single bun test command

Consequences

Positive

  • Single toolchain: bun install, bun dev, bun test, bun run build — no config files for each tool
  • 3× faster installs than npm; lockfile is a single bun.lock
  • TypeScript runs natively — no tsc step in development or tests
  • Native password hashing via Bun.password — no bcrypt native addon
  • .env files loaded automatically — dotenv removed from dependencies

Negative / Risks

  • Bun is newer than Node.js; some npm packages assume Node.js internals and may have edge-case incompatibilities
  • The Bun bundler has fewer plugins than webpack/Vite; complex bundling scenarios may need workarounds
  • Team members unfamiliar with Bun need a short orientation; Node-specific mental models (e.g. process.env vs Bun.env) apply but Bun adds its own APIs
  • Rate-limiting middleware is in-memory (single instance); a Redis-backed alternative is needed for multi-process deployments (documented in the rate limiter source)