How do you release v2 of a module? Compare the "major branch" and "major subdirectory" strategies, and explain +incompatible.
Major branch (the common choice): on main, or on a v2 branch, change the go.mod line to module example.com/lib/v2, update every internal import to the /v2 path, then tag v2.0.0. The code stays at the repo root.
Major subdirectory: copy the code into v2/ with its own go.mod (module example.com/lib/v2). This works with pre-module GOPATH tooling, but you now maintain two copies of the code.
# major-branch workflow
go mod edit -module example.com/lib/v2
# rewrite internal imports (gofmt -r, sed, or a tool such as gomajor)
git commit -am "v2: new module path"
git tag v2.0.0 && git push --tags
+incompatible: if a repo tagged v2.3.0 without any go.mod (a pre-module release), consumers see example.com/lib v2.3.0+incompatible. The go command accepts it under the v1 path but cannot enforce compatibility. When that project later adds a go.mod without /v2, any new v2+ tag is rejected (the module path lacks the major suffix), and once the v1 path has a tagged version with a go.mod, the go command stops choosing +incompatible versions for @latest and -u upgrades.
Interviewers look for this: update the module line and internal imports, consider keeping v1 maintained on a branch, and mark the old version with // Deprecated: in its go.mod to point users to the new one.
More on Modules, Packages & Tooling
- Q408Explain Minimal Version Selection (MVS). How does it differ from npm/Cargo-style resolution?
- Q409What is semantic import versioning, and why must v2+ modules change their import path?
- Q411When would you use a replace directive, and why doesn't a dependency's replace affect your build?
- Q412Explain exclude and retract. Who writes each one, and how do they affect version selection?
- Q413What is a pseudo-version, and when does the go command generate one?
- Q414What problem do Go workspaces (go.work) solve? How do they differ from replace?