Builds had become the quiet tax on every change. A small fix could take long enough to interrupt a train of thought, while larger releases left the whole team watching a progress bar.

Our first instinct was to reach for a new tool. We looked at faster machines, different runners, and a handful of promising replacements for the parts of our pipeline that felt slow. But the stack was not the problem. We simply did not have a clear picture of where the time was going.

Start by measuring, not guessing

We added timing around each stage of the build and began recording the results alongside the commit that produced them. Within a week, the shape of the problem was obvious: a few expensive tasks were being repeated far more often than we realised.

“The fastest build is the one you can prove you never needed to run.”

The largest win came from making the work predictable. We tightened cache keys, separated independent packages, and stopped invalidating the entire graph when a single file changed. None of these changes required a new framework or a migration. They required understanding the one we already had.

After the changes landed, the median build dropped from just over twelve minutes to six. More importantly, the result held across local development and CI. A faster feedback loop made the team more willing to make small changes, which made the code easier to review and the releases easier to trust.