Pipeline cost complaints usually arrive as a number on an invoice with no explanation. Before reaching for a cost tool, read three things in the build log. Each one is free to check and cheap to fix.
Where the money actually goes
Across the pipelines we take over, waste clusters in the same three places. The order below is deliberate: it runs from the cheapest fix to the most disruptive.
| What to look for | How to see it in the log | First fix |
|---|---|---|
| Cache misses | The restore step reports no key found | Pin the cache to the lockfile hash |
| Oversized runners | A build step that finishes in seconds on a 16-core machine | Split test and build onto different sizes |
| Idle previews | Environments older than the branch they belong to | Tear down on merge, not on a cron |
None of these need a new tool. The log you already keep answers all three, and the first two are reversible in a single pull request.
Start with the cache, because it is reversible
A cache key that includes a timestamp, a branch name or a random string will never hit. The step still succeeds, which is why it survives for years: the build gets slower and the bill gets larger, and nothing fails.
The fix is to derive the key from the inputs the build actually depends on. For a JavaScript project that is the lockfile hash; for a container build it is the Dockerfile plus the manifest. Once the key is stable, confirm the restore step reports a hit before moving on.
- Read the restore line, not the save line.
- A key built from anything that changes per run cannot hit.
- Measure the job duration before and after; the saving shows up there first.
Match the runner to the job
The default runner in most repositories is sized for the heaviest job in the pipeline. A twenty-second lint step on a machine built for compiling native code costs the same per minute as the compile, which is where the waste is.
Splitting a workflow by job size is usually a ten-line change: the build keeps the large runner, tests and lint move to the small one. In our experience this is the change with the best ratio of saving to effort, and it is also the least risky, because it cannot alter what the pipeline produces.
Preview environments need an owner
A preview environment is cheap, which is exactly why they accumulate. Twelve idle copies of a staging stack, each with its own database, is a monthly bill nobody approved.
The reliable fix is to tie the environment lifetime to the pull request rather than to a schedule. When the branch merges, the environment goes. Anything that outlives its branch needs a named owner and a reason, and that alone removes most of the cost.
What this does not fix
This check finds waste, not architecture. If a pipeline is slow because a test suite takes forty minutes, the fix is in the test suite, and no amount of runner tuning changes that. The same is true of a build that downloads a dependency tree on every run because it was never vendored properly.
Treat the result as a short list of cheap wins, take them, and then decide whether the remaining cost is worth a deeper change. Most of the time it is not, and the honest answer is that the pipeline is now as cheap as its shape allows.