When our CI pipeline started pushing forty minutes per build, we knew something had to give. But the usual playbook—swapping bundlers, rewriting in Rust, or splitting into micro-frontends—felt like expensive guesswork. We wanted to understand where the time was actually going before we spent any of it.

So we instrumented everything. Not just the high-level steps, but every plugin, every loader, every filesystem walk. What we found surprised us: the bottleneck wasn't our tools, it was how we were using them.

Start by measuring, not guessing

Our first attempt at optimisation was classic cargo-cult engineering. We read blog posts about faster compilers and started drafting migration plans. Then our principal engineer asked a simple question: do we know which minute of the build is the slowest?

We didn't. We had aggregate numbers, not segmented ones. Once we added granular timing to each stage—dependency resolution, transpilation, bundling, minification, compression—the picture changed completely. Two plugins accounted for sixty percent of the total time. Neither was essential.

"The fastest code is the code you don't run. We were running plenty of code we didn't need."

Removing those plugins cut twelve minutes immediately. No stack changes, no migration risk, no training overhead. Just deleted configuration lines that had been copy-pasted from an old template years ago.

The remaining wins came from parallelism we already had but weren't using. Our test runner was CPU-bound on a single core while three sat idle. A one-line configuration change—literally changing workers: 1 to workers: 'auto'—shaved off another eight minutes.

By the time we were done, we'd halved our build time without touching a single dependency version. The lesson generalises: before you reach for new tools, audit your old ones. The biggest gains are often hiding in plain sight, waiting for someone patient enough to look.