Your CI runs `go build -a` on every commit for a clean build. What does the flag cost, and is it needed?
answer
- the flag says rebuild what is already fine
- the standard library gets recompiled too
- ask what failure it prevents
- the key already covers every input
- persist the cache directory instead
basics
~20 sThe -a flag forces every package to be rebuilt from source, including the standard library, instead of reusing cached results. It buys nothing: the build cache is keyed by a hash of all inputs, so anything that changed already causes a rebuild. Drop it and persist the cache.
solid answer
~60 s`-a` means "rebuild everything, even packages that are already up to date", so the compiler runs over the whole dependency graph and every standard-library package the program touches. On a CI job that already starts with an empty cache, that is minutes of CPU on every commit, several times a day. It is not needed for correctness. The build cache is content-addressed: the key covers source bytes, build flags, target platform, toolchain version and the hashes of dependency results, so a real change produces a new key and a rebuild automatically, and a hit is only reused when every input matched. There is no stale-artefact class of bug for `-a` to protect against. The fix is the opposite of what the flag does: remove it, persist the `GOCACHE` directory between runs so the second and later builds are warm, and key that saved cache on the toolchain version so a Go upgrade starts a clean one. Keep `go clean -cache` for the deliberate case where you want to measure a cold build.
go deeper
Know that -a forces every package to be recompiled, standard library included, and that it is about reuse rather than about correctness.
Explain why it is redundant: the cache key already covers source, flags, target, toolchain and dependency results, so any real change rebuilds on its own.
Diagnose the pipeline rather than the flag — prove equality by comparing artefact hashes, remove the flag, then persist and correctly key the cache directory between runs.
Challenge pipeline steps justified as safety rituals by asking which specific failure each prevents, and set the expectation that in Go, build speed and build correctness are not traded against each other.
## What the flag actually does `go build -a` forces rebuilding of packages that are already up to date. It does not empty anything and it does not change the output; it simply declines to reuse work, re-running the compiler over every package in the graph, standard library included. The results are still written to the cache afterwards, so a subsequent build without `-a` is fast again. That description already explains the cost: the whole build, from scratch, every time. Add a CI container that starts with an empty `GOCACHE` and you are paying full price twice over — once because nothing was cached and once because you asked for nothing to be reused. ## Why people add it The habit is inherited. In build systems where reuse is decided by timestamps or by installed artefacts, stale outputs are a genuine hazard: a clock skew, a partially copied file or a mismatched artefact directory can produce a binary that does not match the source. "Force a full rebuild" is a rational defensive move there, and older Go workflows, before the content-addressed cache existed, had more of that flavour too. It also survives as ritual after one unexplained incident. Someone had a bad build, added `-a`, the problem went away for unrelated reasons, and the flag stayed in the pipeline where nobody could argue with it. ## Why it is unnecessary in modern Go The cache is keyed by a hash over every input to each build action: - the source file bytes, - the build flags in effect, including anything coming from `GOFLAGS`, - the target `GOOS`/`GOARCH` and related settings, - the toolchain's own version and identity, - the hashes of the outputs of the packages depended upon. Because dependency output hashes participate, a change anywhere propagates upward: edit one leaf package and every package above it gets a new key. Because the toolchain version participates, upgrading Go invalidates everything without any manual step. Because flags participate, changing how you build invalidates too. The converse is the part that answers the question: **a hit occurs only when every input matched**, and when every input matched, recompiling would have produced the same bytes. So `-a` cannot turn a wrong build into a right one. It can only turn a fast build into a slow one. You can prove this on your own project rather than arguing about it: build normally, build again with `-a`, and compare the SHA-256 of the two binaries. They match. That experiment is worth running once in front of the team that owns the pipeline, because it converts a belief into a measurement. ## What to do instead 1. **Remove `-a`.** Measure the pipeline before and after so the saving is on record. 2. **Persist `GOCACHE` between CI runs.** Save and restore that directory as part of the job. This is where the real win is: the flag was hiding the fact that the cache was cold anyway. 3. **Key the saved cache sensibly** — include the toolchain version and the target platform in whatever key your CI uses, so a Go upgrade or a cross-compilation target starts from a fresh cache instead of dragging a useless one around. 4. **Bound its growth.** The `go` command trims unused entries on its own, but a restored-and-resaved cache in CI can accumulate; letting the saved copy expire periodically is enough. 5. **Keep `go clean -cache` for deliberate cold-build measurement**, not as a routine step. If you want to know what a first-time contributor's build costs, that is how you find out. ## When forcing a rebuild is legitimate Rare, but real: you suspect a toolchain bug and want to eliminate reuse as a variable; the cache directory lives on storage you do not trust, such as an aggressively shared or flaky network volume; or you are bisecting a build problem and want to remove one degree of freedom. Those are investigations, and the flag belongs in the investigation, not in the pipeline that runs three hundred times a day. The general principle is worth stating plainly, because it generalises past this flag: in Go, build speed and build correctness are not in tension. Reuse is safe by construction, so there is no honest reason to trade one for the other — and any pipeline step justified as "to be safe" should be asked what specific failure it prevents.
- How would you convince a sceptical team that dropping `-a` is safe?Run the experiment in front of them: build the same commit once normally and once with `-a`, then compare the SHA-256 of both binaries. They match, because a cache hit only happens when every input hashed the same. Then show the wall-clock difference on a real pipeline run.
- If `-a` is removed, why might CI still be slow, and what fixes that?Because a fresh container starts with an empty `GOCACHE`, so every run is cold regardless of the flag. Saving and restoring that directory between runs is the actual fix; key the saved copy on the toolchain version and target platform so an upgrade starts a clean cache rather than a useless one.
- When is forcing a full rebuild genuinely justified?When you are investigating rather than shipping: eliminating reuse as a variable while chasing a suspected toolchain bug, or working on storage you do not trust to hold the cache intact. Those are one-off diagnostics, not a step that belongs in a pipeline running hundreds of times a day.
- Does `go build -a` empty the build cache?No. It declines to reuse existing entries for this build, but the cache is untouched and the freshly compiled results are written into it, so the next build without the flag is warm. Emptying is a separate, explicit act: `go clean -cache`.
It is like re-verifying a sealed parcel by unpacking and repacking it on every delivery: the seal already told you nothing changed, so the work only costs time.
saying these in an interview costs you the question
- Believes forcing a rebuild protects against stale cached results
- Confuses -a with go clean -cache and thinks it empties the cache
- Thinks a cache hit can yield different bytes than a rebuild
- Adds -a after one unexplained build failure and never removes it
- Optimises the flag but leaves CI starting with an empty cache every run