For most of last year, our builds were getting slower in ways that were easy to notice but hard to explain. A change that once took six minutes to verify had crept past twelve. Nothing in our stack had fundamentally changed, yet every release carried a little more waiting with it.

The obvious response was to reach for new tools. We considered faster runners, a different bundler, and a larger cache. Instead, we paused and looked closely at the system we already had. The biggest gains, it turned out, were hiding in ordinary places: repeated work, broad dependency rules, and defaults nobody had questioned in years.

Start by measuring, not guessing

Our first step was to make the build visible. We added timing around each stage, recorded cache hits, and tracked the files that caused downstream tasks to run. Within a week, the shape of the problem was clear. A handful of small jobs were invalidating most of the pipeline, while several expensive steps were running whether their inputs had changed or not.

“The fastest task is not the one you optimize. It is the one you discover you never needed to run.”

We narrowed task boundaries, made inputs explicit, and moved work out of the critical path. None of these changes was dramatic on its own. Together, they reduced median build time from twelve minutes to just under six, with the slowest builds improving even more.

The lesson was less about performance than attention. Before replacing a system, learn where it spends its time. Better measurements gave us better questions—and those questions were enough to make the stack we already trusted feel fast again.