Every engineering team I've worked on eventually has the same conversation. CI has got slow. Nobody knows exactly where the time goes. Somebody suggests throwing a bigger runner at it, and that buys a few months.
Meziantou recently published Meziantou.GitHubActionsTracing, which converts GitHub Actions runs into trace data — HTML, Chromium traces, Speedscope, OpenTelemetry. That framing is the right one, and it's worth writing about why: a slow pipeline is an observability problem, and most teams attack it as a performance problem.
Logs Answer the Wrong Question
CI logs are a transcript. They tell you what happened, in order, in text.
What you need to know is where the time went — and time is a shape, not a sequence. Which steps ran in parallel and which serialised behind each other. Which job everything else waited on. Whether that fourteen-minute run is one slow step or forty mediocre ones. Whether the slowest step is slow every time or only when the cache misses.
None of that is legible in a transcript, which is why the usual approach is someone scrolling through timestamps doing subtraction in their head. That's the same reason we stopped debugging distributed systems with logs alone and started using traces. CI is a distributed system — parallel jobs, dependencies, shared caches, external services — and it deserves the same treatment.
Once a run is a trace, the questions become answerable by looking: the critical path is the longest bar, and everything not on it is noise you can stop optimising.
What Usually Turns Up
In my experience the wins are rarely where the team assumed:
Dependency restore, not compilation. Teams optimise build flags and ignore that they're re-downloading the world every run because the cache key includes something volatile.
Serialisation that didn't need to exist. Jobs declared to depend on each other out of caution, forming a chain where a fan-out would do. This is usually the single biggest win and it's a one-line change to the workflow file.
Container and image pulls. Invisible in logs, plainly visible in a trace, and often a large fraction of a short job.
Setup steps on every matrix leg. A thirty-second toolchain install is nothing, until it's nothing multiplied by twelve.
The test that got slow gradually. Nobody notices a suite going from four minutes to nine over a year. A trace makes the distribution obvious in a way a pass/fail badge never does.
Flakiness Shows Up Too
The second thing trace data gives you is a handle on flaky tests, which cost more than slow ones.
A slow pipeline wastes minutes. A flaky pipeline wastes trust — and once a team stops believing red means broken, the suite has stopped doing its job entirely. People re-run until green, and eventually a genuine failure gets re-run until green too.
Traces across many runs show variance, not just duration. A step whose time is bimodal is telling you something: a cache that sometimes misses, a service that sometimes throttles, a container that sometimes starts cold. That's the signal you need to fix the cause instead of adding a retry.
Treat Pipeline Time as a Real Metric
The reframe I'd argue for: CI duration is a product metric for your engineering organisation, not a piece of infrastructure trivia.
It sets the cost of every change. A twenty-minute pipeline doesn't just cost twenty minutes — it changes behaviour. People batch changes to avoid waiting. Larger batches are harder to review and riskier to deploy. Riskier deploys make people batch more. The pipeline quietly sets your deployment frequency and your change failure rate, which are the two numbers I actually watch.
So it's worth instrumenting on the same terms as production:
- Measure continuously, not during the quarterly complaint. You want the trend, and you want to notice the slow drift rather than the cliff.
- Alert on regression. A step that doubles should page somebody, the same as a production latency regression would.
- Optimise the critical path only. Everything else is a distraction, and a trace tells you which is which at a glance.
- Fix flakiness before speed. A fast suite nobody believes is worth less than a slow one they do.
The Cheap Version
If you're not ready to add tooling, you can get most of the diagnostic value by asking three questions of your slowest workflow: what is the longest single chain of dependent jobs, what fraction of each job is setup rather than work, and which steps have high variance between runs.
You can answer all three by hand once. The reason to use a tracing tool is that you'll need the answers again in three months, and by then the shape will have changed.
Source: Visualize GitHub Actions runs with Meziantou.GitHubActionsTracing, Meziantou's blog.



