[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"post-content-bun-1-4-rust-rewrite-node-compatibility":3},"\u003Cp>Somewhere in the last twelve months, my JavaScript toolchain stopped being written in JavaScript. Vite 8 runs on Rolldown, which is Rust. TypeScript 7 compiles with a Go binary. And last week Bun 1.4 shipped, replacing roughly 535,000 lines of Zig with over a million lines of Rust. I already wrote about what the \u003Ca href=\"\u002Fposts\u002Fvite-8-rolldown-upgrade-what-actually-changed\u002F\">Rolldown switch did to my builds\u003C\u002Fa> and what \u003Ca href=\"\u002Fposts\u002Ftypescript-7-native-compiler-what-changes\u002F\">the native TypeScript compiler changed\u003C\u002Fa>. Bun was the last domino, so I spent the weekend pointing my Node API at it.\u003C\u002Fp>\n\n\u003Cp>Short version: it runs. Not everything survived, and the things that broke were exactly the boring ones nobody writes benchmark posts about.\u003C\u002Fp>\n\n\u003Cfigure>\n  \u003Cimg src=\"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c?w=1200&q=80&fm=jpg&fit=crop\" alt=\"Terminal code on a dark monitor, the view during a runtime upgrade\" loading=\"lazy\" \u002F>\n\u003C\u002Ffigure>\n\n\u003Ch2>The rewrite itself matters less than you think\u003C\u002Fh2>\n\n\u003Cp>Yes, the Zig to Rust migration was a genuinely wild event. The team did it with AI assistance, the diff made half of Hacker News angry, and people counted the unsafe blocks like they were auditing a prison. I get it. But as a user, the language underneath is not my problem. Whether my app still runs is.\u003C\u002Fp>\n\n\u003Cp>That's where the release notes actually get interesting. Bun 1.4 passes 1,517 more tests from the official Node.js test suite than 1.3 did. Idle CPU dropped about fivefold, memory up to 35%, and Linux startup for a hello world went from 10.9ms to 5.1ms. The number I trust most is not from a synthetic benchmark: Anthropic runs Claude Code on the Rust build, and its production p99 CPU fell from 24% to 10% after the switch. That's a real, long-running app under real load.\u003C\u002Fp>\n\n\u003Cp>Module coverage is where it gets concrete. \u003Ccode>node:events\u003C\u002Fcode>, \u003Ccode>node:trace_events\u003C\u002Fcode>, and \u003Ccode>node:sqlite\u003C\u002Fcode> pass 100% of their test suites. \u003Ccode>node:http\u003C\u002Fcode>, \u003Ccode>node:fs\u003C\u002Fcode>, and \u003Ccode>node:stream\u003C\u002Fcode> sit around 97%. That last 3% is where your weird dependency lives. It always is.\u003C\u002Fp>\n\n\u003Ch2>What actually broke for me\u003C\u002Fh2>\n\n\u003Cp>I pointed a modest Express-style API at it. Application code ran untouched on the first try, which honestly surprised me. Then the environment got involved.\u003C\u002Fp>\n\n\u003Cp>First, env vars. When you run a script with \u003Ccode>bun\u003C\u002Fcode> directly, it auto-loads \u003Ccode>.env\u003C\u002Fcode>. But when Bun runs as a drop-in replacement for \u003Ccode>node\u003C\u002Fcode>, it stopped doing that in 1.4. My config came up empty and I spent an embarrassing ten minutes blaming the wrong thing:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\"># this does NOT load .env anymore\nbun run server.js\n\n# this does\nbun run --env-file=.env server.js\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Second, native modules. Bun now aligns its ABI with Node 26, so \u003Ccode>NODE_MODULE_VERSION\u003C\u002Fcode> moved to 147. Anything with a prebuilt binary, like better-sqlite3 or your favorite image library, needs a matching build. If a package doesn't ship one for 147, you're compiling from source or you're stuck.\u003C\u002Fp>\n\n\u003Cp>Third, YAML parsing got stricter. \u003Ccode>Bun.YAML\u003C\u002Fcode> follows YAML 1.2 now, which means \u003Ccode>yes\u003C\u002Fcode>, \u003Ccode>no\u003C\u002Fcode>, \u003Ccode>on\u003C\u002Fcode>, and \u003Ccode>off\u003C\u002Fcode> are booleans instead of strings. One of my config files relied on the old behavior. It failed loudly, which I appreciated more than I expected to.\u003C\u002Fp>\n\n\u003Cp>Finally, new lockfiles are version 2 with tighter integrity checks, and brand new monorepos default to an isolated linker instead of the hoisted layout. Existing lockfiles keep the old behavior, so this mostly hits you on greenfield projects.\u003C\u002Fp>\n\n\u003Ch2>The twenty minutes I now spend before switching runtimes\u003C\u002Fh2>\n\n\u003Cp>I've been burned enough times that I do the same drill every time a shiny runtime lands. It fits in a coffee break:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">git checkout -b try-bun-1.4\n\n# does everything install?\nbun install\n\n# does the suite pass? run it twice, once with each runtime\nbun test\nnpm test\n\n# does the app boot and answer a request?\nbun run dev\ncurl -s localhost:3000\u002Fhealth | jq .status\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>If the test suite passes on both runtimes, you've answered the only question that matters: not \"is Bun faster,\" but \"can I switch back if this goes sideways.\" The whole argument for Bun in 2026 stopped being benchmark charts and became something you can verify against your own code. That's the part I find genuinely new.\u003C\u002Fp>\n\n\u003Ch2>Where I actually use it now\u003C\u002Fh2>\n\n\u003Cp>My split looks like this. CI and throwaway scripts: Bun, no hesitation, installs alone justify it. Local dev servers: Bun. Production traffic for the API with native dependencies: still Node, until I have a maintenance window and a real reason. That's not fear, that's just scheduling.\u003C\u002Fp>\n\n\u003Cp>One last thing I've started doing: every runtime gotcha like the \u003Ccode>.env\u003C\u002Fcode> change goes into a \"runtime gotchas\" snippet folder I keep in \u003Ca href=\"\u002Fsnippetark\u002F\">Snippet Ark\u003C\u002Fa>, tagged by tool and version. Runtimes are shipping fast enough now that last month's mental model is stale by default. A searchable list of \"this changed in 1.4\" beats my memory every time.\u003C\u002Fp>\n\n\u003Cp>Bun 1.4 is the first version where \"just try your Node app on it\" is reasonable advice instead of a dare. Try it on a branch first. Then tell your native modules I said hi.\u003C\u002Fp>\n",1788507918413]