DevOps

Reduce a slow build by measuring its actual stages

Kamruz
Kamruz
Oct 14, 2026
5 min read
Reduce a slow build by measuring its actual stages

A build taking longer than expected invites quick fixes: add more runners, cache everything, or remove checks. Those changes may reduce the visible duration while creating stale dependencies or skipping useful evidence. The first step is to measure where the time goes and distinguish one-time work from repeated work.

Consider an illustrative web application build in CI. The run includes dependency installation, linting, type checking, compilation, tests, and artifact upload. The same label build may hide several independent stages. No benchmark improvement is claimed here. The purpose is to produce a reproducible comparison and choose a bounded change that preserves the artifact and verification contract.

Split elapsed time into useful stages

Record start and finish times for checkout, runtime setup, dependency installation, validation, compilation, tests, and upload. Include queue wait separately from execution time. A slow job waiting for a runner needs a different response from a compiler consuming CPU. Inspect existing CI timing before adding instrumentation.

Compare several representative runs and note cold versus warm cache conditions. A single unusually slow network request is not proof that compilation is the bottleneck. Record source revision, runner environment, lockfile identity, and whether the run repeats an unchanged commit. This evidence makes later comparisons meaningful. Avoid optimizing the stage with the most familiar tool if another stage dominates the actual elapsed time.

Diagnose dependency installation

Check whether the workflow uses the lockfile and reproducible install command appropriate to the package manager. Repeated resolution, missing caches, or unnecessary development environment setup can add time. Verify which dependencies are needed for build and which for runtime. Removing a package from the image is not the same as removing it from the build environment.

When caching downloaded packages, choose a key that accounts for relevant lockfile and platform changes. Do not reuse installed native artifacts across incompatible runtimes or architectures. Keep cache misses observable. A cache that occasionally restores stale state can cost more debugging time than it saves. The build should still succeed correctly from a clean state, because caches are performance aids rather than required sources of truth.

Inspect compilation and duplicate work

Look for validation repeated by multiple commands. A build tool may type-check while a separate job already performs the same check. Repetition can be intentional, but it should have a reason. Check whether several jobs each install identical dependencies and rebuild the same artifact before testing it.

Measure module count, generated files, and unnecessary inputs when compilation dominates. A large build context or unbounded content generation can create work unrelated to the changed application. Do not remove validation simply because it takes time; identify the evidence it supplies and whether a more efficient arrangement preserves it. A practical artifact lists repeated work, proposed consolidation, and the failure class that must still be detected after the change.

Use Docker cache deliberately

For container builds, inspect layer invalidation. Copying the entire source tree before dependency installation may cause a source edit to invalidate an expensive install layer. Arrange the Dockerfile so stable inputs and changing inputs have clear boundaries where appropriate. Keep the build context limited to necessary files.

Do not assume every cache hit is safe. Changes to dependencies, build arguments, or generated assets must invalidate the relevant result. Secret values should not become ordinary layer contents or cache keys. Verify the resulting image contains the expected output and no unintended source or credentials. Consult the actual builder documentation for supported cache behavior rather than copying an optimization from a different tool and assuming it applies.

Compare one change with a clean baseline

Choose a small hypothesis, such as caching package downloads or sharing one built artifact between validation and deployment. Predict which stage should improve and what correctness risk it introduces. Run before and after under comparable conditions, including clean and warm-cache paths when relevant.

Record elapsed time without inventing a percentage improvement from incomparable runs. If runner variation is large, report that limitation and collect enough evidence to understand the direction. Verify a dependency change and a source-only change, since they exercise different invalidation paths. A fast unchanged rebuild does not prove that a new dependency will be included correctly. Keep the comparison focused enough that a reviewer can attribute the result to the proposed mechanism.

Protect artifact correctness

Check artifact identity, required files, runtime startup, and relevant tests after optimization. If jobs reuse an artifact, make sure it belongs to the current source revision and configuration. Avoid rebuilding separately during deployment in a way that produces a different artifact from the one tested.

Test a deliberate input change that should invalidate the cache and confirm the output changes. Also test a clean build with caches unavailable. Caches can disappear or fail to restore, so the pipeline must remain functional. Connect this to container deployment diagnosis: development mounts and stale layers can conceal missing production output until the server runs the artifact independently.

Balance speed, cost, and maintenance

Parallel jobs can reduce elapsed time while increasing runner use and repeated setup. Larger runners may help CPU-bound compilation but do little for remote provider latency. Remote caches add access control, storage, and invalidation responsibilities. Select the mechanism that addresses the measured stage within the team's operating capacity.

Keep the optimization understandable. A few clear cache keys and one artifact flow are often preferable to a custom dependency graph nobody can debug. Document when caches should be invalidated and how to reproduce a clean run. Revisit the decision when the build process changes, not whenever one run is noisy. The objective is dependable feedback with acceptable latency, not the smallest displayed duration at any cost.

Use AI to inspect timing and workflow

Give an assistant the stage timings, package scripts, Dockerfile, and workflow with sensitive values removed. Ask it to identify repeated work and propose hypotheses tied to measured stages. Require it to explain cache invalidation risks and cite official documentation for specific configuration fields.

Have it draft a comparison plan including cold-cache, warm-cache, source-change, and dependency-change cases. Review suggestions that remove tests or validation particularly carefully. AI should not claim a faster build until the experiment runs. Its useful role is to connect timing evidence to a bounded change and preserve the correctness conditions that make the build worth speeding up.

Sources

Docker build cache documentation is the primary reference for container cache behavior.

GitHub Actions workflow concepts provide CI context. The timing plan and comparison cases above are illustrative, with no claimed benchmark result.

About the Author

Kamruz

Kamruz

Full Stack Developer

A passionate developer who loves creating modern web applications and sharing knowledge with the community.

More About Me

Related Articles