All posts
Software

Why everyone is rewriting their tooling in Rust

From bundlers to linters, the JS ecosystem is going native. Here's the performance story behind the migration.

Dhileep Kumar6 min read
Why everyone is rewriting their tooling in Rust

For a decade, the tools that built JavaScript were themselves written in JavaScript. Webpack, Babel, ESLint, Prettier — all running on the same single-threaded runtime as the apps they processed. It worked, right up until projects got big enough that “wait for the build” became a measurable part of every engineer’s day.

Now a quiet rewrite is underway. Bundlers, linters, formatters, and package managers are being rebuilt from scratch in native languages — overwhelmingly Rust — and the speedups aren’t incremental. They’re 10x, 50x, sometimes 100x. Here’s why it’s happening, what it actually buys you, and where the hype outruns reality.

The JavaScript tax

JavaScript is a fine language to ship. It’s a poor language to build a compiler in. It’s single-threaded by default, so a linter checking 5,000 files can’t trivially spread the work across your CPU cores. Its garbage collector introduces pauses you can’t control. And every tool pays a startup cost to parse and JIT-compile itself before it does a single unit of useful work.

For a small project none of this matters. For a monorepo with tens of thousands of modules, it compounds into minutes per build and seconds per save — the kind of latency that quietly breaks flow and pushes people toward fewer, bigger, riskier commits.

What Rust actually buys you

Rust is attractive here for reasons that have little to do with fashion. It compiles to native code with no runtime and no garbage collector, so performance is predictable. Its ownership model makes parallelism safe enough that tools genuinely use all your cores. And it ships as a single static binary — no node_modules, no version of itself to bootstrap first.

  • No garbage collector — no unpredictable pauses in the middle of a build.
  • Fearless concurrency — work spreads across every core without data races.
  • A single native binary — nothing to install on top of, nothing to warm up.
  • Memory safety without a runtime — close to the speed of C with far fewer footguns.
  • A serious ecosystem — fast parsers, allocators, and async runtimes already exist.

Go got there first for some tools — esbuild proved native bundling could be 100x faster — but Rust has become the default for the current wave, partly because it interoperates cleanly with the existing Node ecosystem through N-API bindings.

A tour of the rewrites

Once you start looking, the pattern is everywhere. Each JavaScript-on-JavaScript tool now has a native challenger, and in a growing number of cases the challenger has already won.

  • Bundling: Webpack → Turbopack and Rspack; Rollup is being reimplemented as Rolldown.
  • Transpiling: Babel → SWC, now the engine inside Next. js and plenty of others.
  • Linting & formatting: ESLint + Prettier → Biome and Oxc, which fold both jobs into one fast pass.
  • CSS: sprawling PostCSS pipelines → Lightning CSS.
  • Beyond JS entirely: Python’s toolchain is getting the same treatment with Ruff and uv, both Rust.

The throughline is consolidation. A single Rust tool often replaces three or four JavaScript ones, because once parsing is cheap you can afford to lint, format, and transform in the same traversal instead of re-parsing the file for each.

The bottleneck was never the language you ship. It was the language your tools were written in.

It isn’t free

There’s a real cost on the other side of the ledger. Rust has a steeper learning curve than JavaScript, which shrinks the pool of contributors who can fix your bundler when it breaks. The N-API boundary between Rust and Node adds its own complexity and occasional bugs. And a from-scratch rewrite means re-learning a decade of accumulated edge cases — the plugin that worked in Webpack may not have an equivalent yet.

Rewrites also fragment the ecosystem during the transition. For a year or two you live in a world where some plugins are native, some aren’t, and the compatibility shims between them are exactly where the bodies are buried.

What it means for you

You almost certainly don’t need to learn Rust. The whole point of this wave is that you reap the speed without touching the language: you swap ESLint for Biome in your config and your CI gets faster. The shift is happening underneath your tools, not in your day-to-day code.

bash
# One native binary replaces eslint + prettier (and their plugin zoo)
npm i -D --save-exact @biomejs/biome

# Lint AND format in a single pass — typically seconds, not minutes
npx biome check --write .
  1. Adopt the native tools where they’re stable — formatters and linters are the safest, highest-leverage swaps today.
  2. Lean toward an all-in-one like Biome or the Oxc family; consolidating tools cuts config sprawl as much as it cuts build time.
  3. Keep an escape hatch — these projects move fast, and not every niche plugin has made the jump yet.
  4. Measure before and after. The wins are real, but so is the cost of a migration that breaks one obscure workflow.

The era of writing your build tools in the language they build is ending. It served the ecosystem well when projects were small, and it’s quietly retiring now that they aren’t. The keyboard still types JavaScript — the machine underneath just stopped speaking it.

Share

Enjoyed this?

Get the next deep dive in your inbox. No spam — just the stories worth reading.

Subscribe to the newsletter

Comments