Bun hit its 1.0 release this year with a strong claim: a faster JavaScript runtime with near drop-in Node.js compatibility, built-in bundling, a built-in test runner, and package management, all without reaching for separate tools for each. We moved a small, low-risk production API over to test that claim against a real workload rather than a benchmark suite, since benchmarks tend to flatter whatever runtime published them.
Startup time and raw request throughput were both noticeably better than our existing Node setup, with no code changes required beyond swapping the runtime and updating the deployment scripts that invoked it. Cold start specifically improved enough that it changed our thinking about Bun for short-lived, invocation-heavy workloads, not just long-running servers, since a faster boot matters disproportionately for anything spun up and torn down frequently.
A couple of npm packages relying on Node-specific native bindings didn't work out of the box, which is the honest caveat behind the "drop-in" marketing. One dependency using a native addon for image processing needed to be swapped for a pure-JavaScript alternative before the migration could proceed, a small but real piece of unplanned work. Bun's built-in APIs for file I/O and HTTP serving are also not always byte-for-byte identical to Node's, and we found two small behavioral differences in edge-case error handling that only surfaced under our existing test suite, which was itself one of the more reassuring parts of the migration, the tests caught the discrepancies before anything shipped to production.
The built-in test runner and bundler were pleasant surprises beyond the raw performance question. Consolidating what had been three separate tools, a test framework, a bundler, and a package manager, into one binary simplified the project's dependency tree and cut a few seconds off every CI run, a small win on its own but one that compounds across a team running CI dozens of times a day.
For greenfield services without exotic native dependencies, Bun is fast enough and compatible enough to be a serious default choice now, and we'd reach for it on a new internal service without much hesitation. For anything with deep Node-specific tooling, established native addons, or a large existing test suite tightly coupled to Node's exact runtime behavior, budget real time to find and patch the gaps before committing to a migration date, and don't assume "drop-in" means zero-diff in practice.
Rolling the change out to the team took less adjustment than we expected. Most day-to-day commands, running the dev server, running tests, installing a package, map onto near-identical Bun equivalents, and the muscle memory transfer was quick for everyone who'd been living in the Node and npm world. The one habit that took longer to break was reaching for separate tools out of instinct, someone would go to install Jest before remembering the built-in test runner already covered the case, which is a good problem to have but a real one during the first couple of weeks.
Deployment required a small amount of extra care. Our hosting provider's build process assumed a Node runtime by default, and getting Bun recognized correctly in the build pipeline took a bit of configuration that wouldn't have been necessary with Node, a reminder that runtime maturity isn't just about the runtime itself but about how well the surrounding hosting and tooling ecosystem has caught up. That gap is closing quickly given how much attention Bun's 1.0 release generated, but it wasn't zero-friction at the time we made the switch.
We're treating this migration as a template for evaluating other services rather than a one-off experiment. The next candidates are a couple of internal tools with light dependency footprints, chosen specifically because they're low-risk enough that any remaining rough edges in Bun's compatibility story would surface without threatening anything customer-facing.