Vol. III · Issue 7 Tuesday, 26 September 2023
ORDR

A point-of-sale, in print and on the floor

Switching to Bun: how moving off Yarn cut our local dev startup by 60%

After two years on Yarn, we moved ORDR's JavaScript toolchain to Bun in late 2023. Install time dropped by an order of magnitude, local dev server startup more than halved, and the migration itself took an afternoon. Here is what we changed, what we measured, and what we still use Node for.

CB

By Carlos Butler

Co-founder, engineering

Tuesday, 26 September 2023 5 min read
An editorial illustration of a small fast bullet train arriving at a softly-lit station alongside an older slower train at dusk, conceptual representation of a tooling upgrade
An editorial illustration of a small fast bullet train arriving at a softly-lit station alongside an older slower train at dusk, conceptual representation of a tooling upgrade

Toolchain choices are unsexy. They are also one of the highest-leverage decisions a small team makes — every developer pays the cost of a slow install every day, every CI build pays the cost of a slow lockfile resolve every push. ORDR ran on Yarn for nearly three years; in September 2023, we moved to Bun. The dev experience improvement was large enough that this post exists.

What we had before

ORDR’s frontend is React 19 + TypeScript + Redux Toolkit, bundled with esbuild, shipped via the Rails asset pipeline. Standard contemporary monorepo-ish setup, except it is one repository, not a monorepo.

Until late 2023, the package manager was Yarn — specifically Yarn 3 (Berry) with the node-modules linker. We had been on Yarn since 2021, switched to it from npm because of a faster install and better script handling. Yarn worked. It was not the problem in 2021.

By mid-2023, the problem was getting clearer:

  • A clean install took 90–120 seconds on a recent MacBook Pro.
  • A dev server startup, post-install, took 8–10 seconds for the cold case (Yarn → esbuild → Rails Puma → assets ready).
  • CI runs for the JS lint/test job spent 60+ seconds on yarn install --immutable even with full cache hits.
  • Yarn 4 was promising improvements but we were on 3 and the migration path was fiddly.

None of this was awful. All of it was friction, paid every day.

Why Bun was a credible candidate

Bun 1.0 shipped on 8 September 2023. Two things made it credible as a replacement for our toolchain:

It is a drop-in package manager. bun install reads the same package.json that Yarn reads, produces a bun.lock instead of yarn.lock, and resolves the same dependency tree. The runtime piece (Bun-the-JS-runtime, replacing Node) is an optional second step. We took the first without the second.

The published numbers were not subtle. Bun’s own benchmarks page claims bun install is 17–80x faster than yarn install for clean installs, and 2–4x faster than pnpm. These are vendor numbers and should be discounted, but the directional gap is large enough that even a 50% discount leaves a material improvement.

The realistic risk was incompatibility — Bun was a new project with a smaller ecosystem of testing than Yarn or npm. We assumed a 20% chance we would have to back out within a week.

The migration

The migration itself took about an afternoon. The full set of changes was small:

  1. bun install instead of yarn install — created a bun.lock.
  2. Removed yarn.lock, .yarn/, and .yarnrc.yml.
  3. Updated package.json scripts that called yarn directly to call bun (most did not — most called node or tsx directly).
  4. Updated the Dockerfile to install Bun via the official install script instead of Yarn.
  5. Updated the GitHub Actions workflows to use oven-sh/setup-bun instead of actions/setup-node for the install step.

That was it. No source-code changes. No esbuild config changes. No Rails-side changes. The application built and ran first time on Bun’s package manager.

What we measured after

Same MacBook Pro, same project, same time of day, same warm npm cache:

StepYarn 3Bun 1.0Change
Clean install (no cache)112s9s12x
Re-install (warm cache)18s1.4s13x
bun run dev cold start (post-install)8.2s3.1s2.6x
CI install step (cache hit)64s7s9x

The 60% number in the title refers specifically to the cold-start time from bun install through to “esbuild server is serving”, which is the developer-experience metric that actually matters for “did I just rebase”.

A widely-cited 2024 post by Theo Browne on switching to Bun reports similar numbers. So does Replit’s writeup of using Bun in their build infrastructure. Vercel’s own performance comparisons are more conservative but still show Bun ahead for cold installs. The general shape of “Bun wins on installs, less clearly on long-running processes” is well-attested by now.

What we did not change

We are still on Node as the runtime for production. The frontend bundle is compiled by esbuild — Bun is not in the production request path. We considered it. Node 20 (and now Node 22) has matured to the point where the TechEmpower benchmarks show Node and Bun within a small fraction of each other on most realistic web workloads, and Node has a fifteen-year operational pedigree that Bun is still building. For a payments-processing application running at a venue, “boring” wins over “fast”.

We also did not switch from npm or yarn workspaces to a different monorepo tool, because we do not have a monorepo. ORDR is a single repository with a single package.json.

What surprised us

Lockfile readability. bun.lock is human-readable JSON-ish text; yarn.lock was reasonably readable but bun.lock is genuinely diffable in a PR. Small thing, real quality of life.

Bun runs TypeScript directly. Our scripts in scripts/*.ts (build helpers, image generators, the wrappers we have since written for various external APIs) run as bun run scripts/foo.ts without a build step. The equivalent on Node requires tsx or a pre-build. The DX improvement on the script side is bigger than the install-time improvement.

Less surprising than expected. We had budgeted for at least one nasty incompatibility — a native module that did not build, or a script hook that did not run, or a CI runner that did not support Bun. None of that materialised. The migration was simply quieter than we had planned for.

What ORDR does about this

ORDR’s frontend toolchain runs on Bun 1.x for installs, esbuild for bundling, and Node 22 in production. The migration paid for itself in developer experience within the first week and continues to pay for itself in CI minutes. If you are a small team still on Yarn or npm and you have not tried Bun for installs, it is genuinely a one-afternoon experiment, and most projects come out the other side faster.

✻ The standing notice

What ORDR does about this.

If you are evaluating tills for a restaurant or a bar and you would rather not gamble on a vendor whose printing layer is held together with third-party middleware, we would be glad to show you ours.