How do you decide which Go test suites gate a merge and which sit behind a build tag?
answer
- what a pre-merge run is actually for
- attributable and fast, not merely fast
- the excluded file is not compiled at all
- two mechanisms with very different costs
- someone owns the scheduled run
basics
~20 sGate merges on suites whose failure the diff explains and a developer can diagnose in minutes; push the rest behind a build tag or a testing.Short guard. The decisive cost is that a tagged file is not even compiled by the default run.
solid answer
~50 sI start from what a pre-merge run is for: catching a defect the author can attribute to their own change while it is still in their head. Anything that qualifies — unit tests, anything under a couple of minutes, anything reliably deterministic — runs in the default `go test ./...`. Everything else gets a mechanism chosen deliberately rather than by habit: `testing.Short()` when I want the code still compiled, vetted and visibly skipped on every run, and `//go:build integration` when the suite genuinely cannot compile or link in an ordinary environment. The build tag is the expensive choice, because an excluded file is not type-checked by anything and quietly rots, so anything behind a tag needs a scheduled tagged build whose failure has a named owner. I also write down what the fast path is allowed to miss, so when an escape happens the conversation is about moving a specific suite rather than assigning blame.
go deeper
Know that not every test runs on every push, and that Go's two opt-in switches are the -short flag with testing.Short and build constraints with go test -tags.
Explain the mechanical difference the decision trades on: a -short skip still compiles and reports itself, while a tagged file is absent from the build entirely.
Argue a concrete split for a real service, and say how the tagged suite stays alive — a scheduled run, a cheap tagged vet on every push, and a failure someone acts on.
Own the tiering as written policy: what the fast path may miss, what that risk buys in cycle time, who owns the scheduled run, and how you revise it after an escape without relitigating every suite.
## The question behind the question Deciding what gates a merge is a release-policy call, and in Go it is unusually concrete because the language gives you exactly three switches: run it always, guard it with `testing.Short()`, or exclude it with a build constraint. Choosing between them is not a style preference — each one buys a different amount of signal at a different price. ## What a pre-merge run is for A pre-merge run exists to catch defects *while the author still has the change in their head*, and to attribute them to that change. That gives two criteria: **attributable** (a failure most likely caused by this diff, not by the environment) and **fast enough to keep the author present**. A suite that fails for infrastructure reasons twice a week fails both criteria no matter how valuable its coverage is, because a gate people learn to re-run is not a gate. Everything that passes both goes in the default `go test ./...`. Note that this is not the same as "everything fast": a two-second test that flakes on a shared external dependency is a worse gate than a forty-second deterministic one. ## Choosing the mechanism for the rest **`testing.Short()` with `-short`.** The file stays in the build. It is compiled, type-checked and covered by the vet pass on every run, refactors reach it, and it prints a SKIP line naming itself. The fast job runs `go test -short ./...`; the full job runs `go test ./...`. Cost: the fast build still compiles and links everything the slow test needs. Use it when the suite *can* run anywhere and you are only buying minutes. **`//go:build integration`.** The file leaves the build entirely. Nothing compiles it, nothing type-checks it, and its absence is unreportable — a `[no test files]` line at best. Cost: the suite rots invisibly, and a green default run is no evidence about it whatsoever. Use it when the suite genuinely cannot compile or link without something an ordinary environment lacks. The honest framing for a team is: `testing.Short()` is the default choice, and a build tag is what you reach for when compilation forces you, because you are paying for it with a whole category of blindness. ## The obligations a tag creates Moving a suite behind a tag is not free CI time; it is a transfer of risk that comes with three obligations, and if you cannot fund all three, do not add the tag: 1. **A scheduled tagged run.** Otherwise the suite is not a suite, it is archived source. 2. **A named owner for that run's failures**, with the same standing as a merge-gate failure. A permanently red nightly is worse than no nightly, because it manufactures confidence. 3. **A compile check on every push.** `go vet -tags=integration ./...` costs seconds and removes the worst property of the tag — that the excluded code stops being checked by anything. ## Writing the policy down The artefact that makes this a policy rather than a habit is a short statement of *what the fast path is allowed to miss*: for example, "pre-merge does not cover real-database behaviour, cross-platform paths, or anything above two minutes." It is worth writing because it makes the tradeoff auditable and it changes the conversation after an escape. Without it, every incident becomes a negotiation about whether someone was careless; with it, the question is whether that class of defect belongs in the fast path now, and what the cycle-time cost of moving it would be. ## Revising it after an escape When a defect reaches production through the fast path, first establish which case you are in. If a suite covering it existed and was tagged out, the argument is about promoting that specific suite and paying its minutes — a bounded, measurable decision. If no such suite existed, adding a fast test is almost always cheaper than reshuffling the tiers, and the policy needs no change at all. Distinguishing those two cases is what keeps the gate from ratcheting upward after every incident until the pre-merge run takes forty minutes and people stop waiting for it. ## The failure mode to watch for The long-run risk is not that the fast path misses something once. It is a tagged suite that nobody has compiled in three months, whose scheduled job was disabled during an unrelated migration, and which is still cited in a design review as evidence that a code path is covered. That is why the mechanism choice and the ownership question are the same question, and why the answer to "can we speed up CI by tagging this out?" is always "who owns the tagged run?".
- Why is moving a suite behind a build tag not a free way to speed up CI?Because it removes the code from the build, not just from the run. Nothing type-checks the tagged file, refactors skip it, and its dependencies drift; three months later the tagged job fails to compile and nobody can say when the suite last passed. Budget the scheduled build and its owner before you add the tag.
- An escape reaches production through the fast path. What do you change?First establish whether a covering suite existed and was tagged out, or never existed at all. If it existed, the decision is whether to promote that specific suite and pay its minutes; if it did not, adding a fast test is cheaper than reshuffling tiers. Change the policy for a class of defect, not once per incident.
- How do you keep a scheduled tagged run from becoming permanently red and ignored?Give it the same standing as the merge gate: a named owner, a failure that generates real work, and a rule that it is fixed before the next run rather than muted. If a suite cannot hold that bar, it is not carrying its weight — promote it into the fast path or delete it, but do not keep it as decoration.
- How would you argue for the -short guard over a build tag to a team that wants both suites separated?Both give you the fast run. Only the guard keeps the slow tests compiled, vetted and named in the output, so they stay refactor-safe and their absence is visible. Reserve the tag for suites that genuinely cannot compile or link in a normal environment, since that is the only thing the guard cannot do for you.
saying these in an interview costs you the question
- Tags out slow suites without scheduling a tagged build
- Thinks a build tag only skips running, not compiling
- Treats a permanently red scheduled job as normal
- Tiers suites by run time alone, ignoring flakiness and signal
- Ratchets the merge gate wider after every single incident