The 2026 Build Time Benchmark
Median build and test wall-clock time by language, and how much of it is setup rather than code.
Executive summary
Build time is the most felt metric in all of CI because every engineer waits on it many times a day, yet most teams benchmark it only against their own past rather than against their peers. This report sets a per-language baseline: the modeled median wall-clock time of a representative build-plus-test pipeline, with the same phases broken out so the comparison is apples to apples. The point is not to crown a fastest language, it is to show a team where its own pipeline sits and which phase is worth attacking first.
The headline is that compiled and interpreted languages diverge far less than teams expect once dependency install and environment setup are counted. A Go pipeline builds fast but pays little for setup, a Rust pipeline pays a heavy compile cost that nothing else can hide, and a JavaScript pipeline spends most of its time installing packages rather than running tests. The cross-language median lands near eight and a half minutes, and a striking share of that, roughly 38 percent, is setup and install rather than the build or test work teams think they are paying for.
That distribution matters because it tells you where the leverage is. If most of your wall-clock is genuine compilation, the fix is incremental builds and shared caches. If most of it is dependency install repeated from scratch on every run, the fix is a warm, persistent cache that survives the ephemeral runner. Teams that profile their pipeline by phase before optimizing almost always find the long pole is not where they assumed it was, and they stop buying bigger runners for problems that bigger runners do not solve.
Because wall-clock is dominated by uncached setup and cold runners, the fastest interventions are caching and warm capacity rather than faster machines. The managed-runner line in this benchmark removes the cold-start and uncached-install penalty by keeping a warm pool and a persistent cache layer in front of otherwise ephemeral jobs. The per-minute rate is also about 69 percent below the hosted blend, so the time saved is cheaper as well as faster, a combination that rarely holds when a team simply upgrades to a larger instance.
The throughline is that build time is a structural property of the pipeline, not a fixed cost of the language. Two teams shipping the same Node service can sit at three minutes and at nine, and the difference is almost never the application code. It is whether the cache hits, whether the runner is warm, whether the matrix is doing redundant work, and whether transient failures force silent re-runs that double the effective wall-clock. Every one of those is addressable without rewriting a test.
Modeled median wall-clock minutes for a representative pipeline in each language. · Source: Latchkey analysis (modeled)
Modeled share of median pipeline wall-clock across phases, cross-language average. · Source: Latchkey analysis (modeled)
Email me the report
The full report is right here on this page, free. Want the link in your inbox to read later or share, plus new Latchkey reports as they drop? Drop your email and we will send it over.
Sent! Check your inbox for the report link.
No spam. Unsubscribe anytime.
Languages converge once setup is counted
The raw build step varies enormously by language, but the full pipeline converges because every stack pays for dependency install, environment setup, and a cold runner. A JavaScript pipeline that compiles almost nothing still lands near the cross-language median because package install dominates its wall-clock. A Go pipeline that compiles quickly is fast end to end only because its module fetch and setup are also light. The shared overhead is the great equalizer.
This is why benchmarking the build step alone is misleading. Teams that quote 'our build takes two minutes' are usually quoting the compile phase and ignoring the four minutes of install, restore, and boot that wrap around it. The number a developer actually waits on is the end-to-end wall-clock, and that is what this benchmark measures: setup plus install plus build plus test, summed.
The practical consequence is that the highest-value optimization is frequently shared across languages even though the build steps look nothing alike. Cut the cold-start penalty and cache the dependency install, and a Node pipeline and a Java pipeline both improve, because the part you fixed was never language-specific in the first place. Language sets the floor of the build phase; the surrounding phases decide whether you are anywhere near that floor.
Cold starts and uncached installs are the long pole
On a cold runner with no cache, a typical Node pipeline spends more time getting ready to work than working. The instance boots, pulls a base image, resolves a dependency graph, and downloads packages it had already downloaded on the previous run, all before a single test executes. None of that is useful output; it is pure setup tax paid on every push.
Warming the runner and caching dependencies together cut the modeled wall-clock from 8.4 minutes to 3.4, close to a 60 percent reduction, without touching a single test. The warm pool removes the boot and image-pull penalty, and the persistent cache turns a multi-minute install into a sub-second restore. These two levers compound: a warm runner with a cold cache still pays the install, and a cold runner with a warm cache still pays the boot.
This is why the fastest teams treat warm capacity and caching as first-class infrastructure rather than as optional optimizations bolted on later. The chart shows the same Node pipeline at four points on that spectrum, and the gap between the cold uncached run and the warm cached managed run is larger than any difference between languages in the benchmark. The runner layer, not the application, is the dominant variable.
- A cold uncached Node pipeline models at 8.4 minutes; warm plus cached on a managed runner models at 3.4.
- The warm pool removes the boot and image-pull tax; the persistent cache removes the dependency install tax.
- The two levers compound, so adopting only one leaves most of the saving on the table.
Same Node pipeline on a cold uncached hosted runner vs a warm cached managed runner. · Source: Latchkey analysis (modeled)
Rust and Java pay the heaviest compile tax
Strongly typed compiled languages sit at the top of the benchmark because the build step itself is expensive and hard to shortcut. A Rust workspace recompiles large dependency trees and runs heavy monomorphization, and a Java build pays for annotation processing and a JVM warm-up before any test runs. These costs are intrinsic to the compile model, not accidents of pipeline shape.
Incremental and shared compilation caches help materially, but the ceiling is higher than for interpreted stacks. A Python or Node pipeline can approach its theoretical floor with caching alone; a Rust pipeline still has to compile changed crates no matter how good the cache is. That means the marginal return on a faster runner is higher for these stacks, because the bottleneck is genuinely CPU-bound compilation rather than I/O-bound install.
The implication for these teams is a different optimization order. Cache the parts that do not change (the dependency compilation, the build cache, the toolchain), then right-size the runner so the changed-code compile finishes fast, and only then worry about test parallelism. Throwing parallelism at a serial compile step buys nothing; persistent build caches and a correctly sized runner buy the most.
Bigger runners hit diminishing returns fast
Adding cores helps until the pipeline runs out of independent work to parallelize, after which wall-clock flattens while cost keeps climbing. In the model, a Java pipeline drops from 11.6 minutes on two cores to 8.7 on four, but only to 7.2 on eight, because its long-pole test shard is serial and no number of extra cores can speed it up.
This is the single most common right-sizing mistake: a team sees a slow pipeline, reaches for the largest runner tier, and pays double for a 10 percent improvement. The cores are real but the work is not parallel enough to use them. Past the knee of the curve, every additional core is mostly cost.
The lesson is to shard tests and right-size the runner rather than reaching for the biggest machine. Once the test suite is split into balanced shards that run concurrently, the pipeline can actually use more cores, and the right runner size becomes the one that keeps every shard busy without idle capacity. A managed layer that right-sizes per job captures that sweet spot automatically instead of defaulting every job to a single oversized tier.
- Java pipeline models at 11.6 min on 2-core, 8.7 on 4-core, 7.2 on 8-core: gains taper sharply.
- A serial long-pole shard caps the benefit; extra cores past that point are mostly cost.
- Shard tests first so work is parallel, then pick the runner size at the knee of the curve.
Median Java pipeline on progressively larger runners; gains taper as parallelism saturates. · Source: Latchkey analysis (modeled)
Faster builds are cheaper builds on managed runners
Speed and cost usually trade off. A larger runner is faster and more expensive; a smaller one is cheaper and slower. The runner layer is the rare place where the two move together, because the wall-clock removed by warm pools and caching is wall-clock you also stop paying for.
The managed line removes cold-start and uncached-install time while charging a per-minute rate about 69 percent below the hosted blend. Using the published rates, a Linux hosted minute costs 0.008 and the managed blended rate models at 0.0025, so the saved minutes are billed at a lower rate on top of there being fewer of them. The effect is multiplicative, not additive.
For a team running thousands of pipelines a week, that combination is the difference between CI being a fixed tax and CI being a controllable line item. Engineers wait less, the invoice shrinks, and neither outcome required rewriting application code or accepting a worse developer experience to save money.
The matrix multiplies whatever your per-leg time is
A single pipeline number hides the fact that most real pipelines run a matrix: several language versions, several operating systems, several dependency sets. The benchmark measures one leg, but the bill and the wall-clock are the per-leg time times the matrix width, and that multiplication is where slow setup becomes genuinely expensive.
If a single leg spends four of its eight minutes on uncached setup, a six-way matrix spends twenty-four minutes per push on setup alone, repeated identically across legs that could have shared a cache. Teams that profile the matrix rather than a single leg frequently discover that the cheapest win is making the legs share warm cache state instead of each paying the cold tax independently.
Operating-system placement compounds this. The OS multipliers from GitHub pricing put a Windows minute at 2 times Linux and a macOS minute at 10 times, so a matrix leg that runs OS-agnostic setup on macOS is paying ten times the rate to do work that would pass identically on Linux. Reserving the premium operating systems for the legs that truly need them, and running setup-heavy work on Linux, often cuts both the wall-clock and the bill of the whole matrix.
Re-runs are the wall-clock teams forget to count
Every benchmark number assumes the job passes the first time. Real pipelines do not. A transient failure, a registry timeout, a network blip, or an out-of-memory kill turns a green run into a red one that a developer re-runs by hand, and the effective wall-clock for that change is suddenly double the benchmark.
Most of these failures were never really broken. They are mechanical and pass on a clean retry, which means the re-run produced no new information; it just paid the full pipeline cost again to confirm what the first run would have shown without the hiccup. A two percent flake rate on a busy repository quietly inflates median time-to-green well beyond the benchmark figure.
Self-healing runners attack this directly by retrying a known-transient failure on a fresh environment automatically, before a human sees a red check. The failed step recovers, the developer never context-switches, and the effective wall-clock stays close to the benchmark instead of being inflated by avoidable repeat runs. A clean benchmark is only realistic if the platform keeps the pipeline from silently paying for itself twice.
Recommendations
Profile your pipeline by phase before optimizing
Break your real pipeline into setup, install, build, and test and measure each. The benchmark shows the long pole is usually install or cold start, not build, and teams that skip this step routinely buy bigger runners for a compile problem they do not have. Find your own phase split first, then attack the largest slice.
Make caching and warm capacity first-class infrastructure
The single largest modeled wall-clock reduction comes from warming the runner and caching dependencies, not from faster cores. Treat the cache key and the warm pool as production infrastructure you monitor, not as a one-time setup, because a cache nobody watches silently stops hitting.
Shard tests, then right-size the runner to the knee of the curve
Extra cores only help if the work is parallel. Split the test suite into balanced concurrent shards, then choose the runner size where wall-clock stops improving meaningfully rather than the largest tier. This buys most of the speed at a fraction of the cost.
Run OS-agnostic work on Linux and reserve premium operating systems
Linting, unit tests, and dependency install pass identically on Linux but cost up to ten times as much on macOS. Push the heavy OS-agnostic legs of the matrix onto Linux and keep macOS and Windows for signing, packaging, and platform-specific tests to cut both wall-clock and bill.
Auto-heal transient failures so re-runs do not double your wall-clock
A clean benchmark assumes first-try success; real pipelines pay for flaky re-runs. Retrying mechanical failures on a fresh environment keeps effective time-to-green close to the benchmark and stops a small flake rate from quietly inflating your median pipeline time.
Outlook
Through 2026 and into 2027, expect the gap between teams that profile and tune their pipelines and those that do not to widen rather than close. Codebases grow, matrices widen, and dependency trees deepen, so a pipeline that was tolerable at four minutes drifts toward fifteen unless someone defends the budget. The teams that have made caching and warm capacity structural will absorb that growth; the teams that treat build time as a fixed cost will feel it as a steadily rising tax on every push.
The architectural direction is toward the runner layer doing more of the work automatically. Warm pools that remove cold starts, persistent caches that survive ephemeral jobs, per-job right-sizing, and self-healing for transient failures are converging into a baseline that a team gets by default rather than by building a platform group. As that baseline spreads, the per-language build floor will matter less than whether a team is sitting at its floor or well above it.
For most teams the practical takeaway is that build time is fixable without heroics. It needs a phase-level profile, a warm cached runner layer, sharded tests, and automated recovery from flakes. The organizations that internalize that will spend the next two years shipping faster while their peers keep waiting on setup they could have cached away.
Methodology
This benchmark models the median wall-clock time of a representative build-plus-test pipeline per language as the sum of setup, dependency install, build, and test phases, in minutes. Phase splits and per-language medians are Latchkey analysis (modeled) from typical pipeline shapes, not a primary survey. Cost commentary ties minutes to the published GitHub-hosted per-minute rates (Linux 0.008, Windows 0.016, macOS 0.08) and the Latchkey managed rate of 0.0025, a 70% reduction versus the blended hosted average; OS multiples (Linux 1x, Windows 2x, macOS 10x) are from GitHub pricing. CI adoption near 76% is from the developer survey. Figures labeled "modeled" are illustrative estimates derived from public pricing and typical pipeline shapes, not a primary survey; figures attributed to a named source reflect that source. Pricing reflects published rates at time of writing and should be verified against current provider pricing.
Sources
- Stack Overflow Developer Survey
- GitHub - Octoverse
- JetBrains Developer Ecosystem Survey
- GitHub Actions - billing & pricing