skip to content

How do you decide whether a shared Go repo commits vendor/, given every dependency bump then lands in review?

level: principalimportance: should knowfreq 28%

answer

  1. two costs, paid by different people
  2. one benefit is only real if someone reads it
  3. ask whether any build has no network
  4. the diff size is the recurring tax
  5. reversible in a single commit

basics

~20 s

Decide by asking who bears each cost. Committing the vendor directory buys builds that need no network and puts every dependency change into code review, at the price of huge diffs, merge conflicts and a second tree to keep in sync with go.mod.

solid answer

~50 s

It is a tradeoff between costs that land on different people. Committing `vendor/` buys builds that need no network at all, so a release can be rebuilt from an old commit on an isolated machine years later; and it puts every byte of every upgrade into a pull request, which is real supply-chain review when someone reads it and pure noise when nobody does. The costs are a repository that grows without bound, review diffs of tens of thousands of lines, merge conflicts on every concurrent dependency bump, and a second artifact that can drift out of sync with `go.mod`. I decide by asking three questions: does any build environment genuinely have no network access; will anyone actually read the vendored diff; and will we pay for the regeneration check in CI that makes the whole thing trustworthy. If all three answers are no, the directory is ceremony and I remove it.

go deeper

for a junior

You will not make this call, but know what it means for you: once the directory is committed, your builds resolve packages from it, and any dependency change you make must be regenerated and committed alongside go.mod.

for a middle

Be able to state both sides concretely — offline builds and visible dependency diffs on one side, repository growth, review noise and merge conflicts on the other — rather than asserting a preference.

for a senior

Show that you would land the CI regeneration check in the same change that adds the directory, and that you would structure commits so a dependency bump is never mixed with product code.

for a principal

Own the asymmetry: you get the offline build, the reviewers get the diff. Test the review benefit with the people who would actually read it, name which of the two arguments you are buying, and say out loud that the decision is reversible in one commit.

## Why this is a decision someone owns Committing a `vendor` directory looks like a build-configuration detail and is not. Because the `go` command switches to vendor mode automatically once the directory exists, adding it changes how every engineer's build resolves packages, and it changes what every dependency upgrade looks like in code review. The build or release owner makes the call; the reviewers pay a recurring tax on it. That asymmetry is what makes it a judgment question rather than a technical one, and it is why the decision can legitimately be overruled by the people reading the diffs. ## What committing it actually buys **A build with no external dependency.** Everything the compiler needs is in the commit. An isolated release machine can produce the artifact; an old release commit can be rebuilt long after the fact without depending on any remote host still serving those versions. This is the strongest argument and the one that most often justifies the whole arrangement on its own. **Visibility of dependency change.** An upgrade stops being a two-line change to a requirements file and becomes a diff containing the actual code that changed. In principle that is supply-chain review: a reviewer can see that a patch release rewrote a networking path or added a new import. In practice this benefit is entirely a claim about human attention, and it is worth exactly as much as the attention it receives. ## What it costs **Repository size, forever.** Vendored source is added, never removed, from history. Clone and fetch times grow monotonically. **Review noise.** A single dependency bump can be forty thousand lines. Mixed into a pull request that also changes product code, it obscures the change that actually needed review — the exact opposite of the stated benefit. **Merge conflicts.** Two branches bumping different dependencies conflict in the vendored tree, and the conflicts are in code nobody on the team wrote. The correct resolution is always "regenerate", which people learn only after resolving a few by hand and getting them wrong. **A second thing that can drift.** The tree can disagree with `go.mod` loudly, which the build catches, or silently, which it does not. Closing the silent gap requires a regeneration check in CI, which is ongoing work the decision commits you to. ## The questions I actually ask **Does any build environment genuinely lack network access?** If a release or air-gapped build really cannot fetch dependencies, the argument is close to settled — this is the one thing vendoring provides that nothing else does. If every build environment has reliable access, this argument is not available and the decision rests entirely on the review benefit. **Will anyone read the vendored diff?** Ask the reviewers, not yourself. If the honest answer is that a large `vendor/` diff is always collapsed and scrolled past, then the review benefit is zero, and pretending otherwise is worse than not vendoring, because the team believes it has a control it does not have. **Will we pay for the check?** Vendoring without a CI step that regenerates the tree and fails on any difference gives you an offline build whose contents nobody has verified. If nobody will own that step, the guarantee is imaginary. **What is the cost profile of this specific repository?** A library imported by many teams and a single deployable service are not the same case. So is a repository with three dependencies versus one with three hundred. ## Making it survivable if you do vendor - Keep dependency bumps in **their own commits**, never mixed with product changes, so a reviewer can skip the vendored diff deliberately rather than accidentally. - Mark the directory as generated so review tooling collapses it by default, making the vendored diff reviewable by exception. - Make the regeneration check a required status, so nobody has to eyeball whether the tree matches. - Write down which of the two arguments — offline builds or diff review — you are actually buying, because the answer decides what you do when the cost rises. ## Reversibility This is not permanent architecture. Removing the directory is a single commit, after which builds go back to resolving from module requirements as normal. Say so explicitly when you make the call: it lowers the stakes, it makes the decision easier to revisit when the build environment changes, and it stops the choice from acquiring more significance than it deserves. ## The stance My default is not to vendor, and to vendor deliberately when an environment genuinely cannot fetch dependencies — with the regeneration check landing in the same change that adds the directory. I treat "it is safer" as an unfinished argument: safer against what, verified by whom, and read by which reviewer.

  • Who can legitimately overrule the build owner on this?
    The reviewers. The review half of the argument is entirely a claim about human attention, and the teams reading the pull requests are the only ones who know whether a forty-thousand-line vendored diff is read or scrolled past. If they say it is always scrolled past, that half of the case is gone and the offline-build argument has to stand alone.
  • How do you make large vendored diffs less painful without dropping vendoring?
    Keep dependency bumps in their own commits so the vendored diff is never mixed with product code; mark the directory as generated so review tooling collapses it; and make the CI regeneration check a required status so nobody has to eyeball whether the tree matches go.mod. The diff then becomes reviewable by exception rather than line by line.
  • What would make you reverse the decision later?
    A build environment that gains reliable access to module sources, which removes the offline argument entirely; or a repository grown to where clone, fetch and merge costs exceed what the guarantee is worth. Reversal is one commit, which is why I say up front that this is a revisitable operational choice rather than architecture.

saying these in an interview costs you the question

  • Argues vendoring is always safer without saying who reviews the diff
  • Presents vendoring as a security control in its own right
  • Ignores the CI regeneration check the decision commits you to
  • Claims vendoring pins versions that go.mod would otherwise float
  • Treats the choice as irreversible architecture
  • Never asks whether any build environment lacks network access