Prompt and context
A multi-module Go monorepo uses Go 1.25. The team wants Go 1.26 tools and runtime improvements, but developer machines range from Go 1.23 to 1.26, CI has Linux, macOS, and Windows runners, and downstream users require library compatibility with older Go versions.
Design a migration that covers the go directive, toolchain selection, the Go 1.26 bootstrap requirement, module release boundaries, a CI matrix, and rollback gates.
What the interviewer tests
- Whether you distinguish compiler version, the language version in
go.mod, and automatic toolchain downloads. - Whether you identify how the Go 1.26 bootstrap requirement affects build images and the self-hosting chain.
- Whether multi-module repositories, generated code, and downstream consumers have explicit compatibility boundaries.
- Whether reproducible CI, checksums, and artifacts prove safety instead of merely updating local Go.
Questions to clarify
- Are there multiple
go.modfiles, tool modules, and generated-code directories? - Are you shipping binaries, libraries, or both? What is the downstream minimum Go version?
- May CI download toolchains automatically, and how does offline build work?
- Are cgo, platform-specific tools, old compilers, or vendor build images involved?
30-second answer
I would inventory each module’s minimum support version, generators, and runners, then manage the Go 1.26 compiler, go directive, and toolchain selection separately. Go 1.26 requires Go 1.24.6 or newer to bootstrap, so build images and the self-hosting chain must be upgraded first. Modules keep the downstream-promised go version until they actually use a newer language or library feature. CI pins toolchains, checksums, and offline caches across Linux, macOS, and Windows, with two-version tests, artifact comparison, and a canary. Rollback restores the image, lockfile, and module directives together.
Step-by-step deep dive
Inventory versions and boundaries
List each module’s go, toolchain, replace directives, generators, cgo settings, and platform constraints. Separate libraries that must remain consumable by old Go from internal tools that must be built with a new compiler instead of raising the entire monorepo.
Fix the bootstrap chain first
Go 1.26 requires Go 1.24.6 or newer for bootstrap. Validate builder images, cross-compilation environments, and self-hosting scripts:
bootstrap Go >= 1.24.6
build Go = 1.26.x
module go = lowest promised language versionCompile and test in an isolated image before replacing shared runners. Record download sources and checksums so the host Go is never used implicitly.
Design go.mod and toolchain policy
The go directive expresses the module language version, while toolchain can express a recommended build toolchain. A library should not raise its go directive merely because CI tooling upgraded; if it uses a Go 1.26 feature, raise the module version and document the minimum. Automatic downloads need a proxy, cache, and offline-failure policy.
Handle multi-module and generated code
Upgrade tool modules first while product modules retain their language promise. Pin generator Go versions, input schemas, and outputs; compare formatting, exported APIs, binary behavior, and source metadata before and after. Do not let a generator silently use a different developer-machine toolchain.
Build a CI compatibility matrix
Test Go 1.25 and 1.26 compilation, unit tests, race, static checks, and packaging on supported operating systems. Run minimum-version consumer tests for libraries and pin 1.26 for internal binaries. Cache keys must include Go version, module graph, and platform to avoid cross-version cache reuse.
Canary, observability, and rollback
Start with one module and one runner canary. Compare compile time, test outcomes, race reports, artifact hashes, startup behavior, and dependency resolution. If bootstrap, cgo, platform, or downstream compatibility fails, restore the old image, go.mod/toolchain, and cache keys; changing PATH alone is not a rollback.
Model answer
The migration must separate compiler, language version, and bootstrap chain. Go 1.26 requires Go 1.24.6 or newer to bootstrap, so upgrade builder images and cross-compilation first. Libraries keep their promised minimum go directive until they truly use a new language or library feature; internal tools may move earlier. Pin toolchains, proxies, checksums, and caches, then test Go 1.25/1.26, supported platforms, race, and downstream minimum versions. Pin generator versions and compare artifacts. Expand from canary metrics, and roll back the image, module directives, toolchain, and cache together for reproducible builds.
Common mistakes
- Upgrading developer Go while ignoring the bootstrap compiler and build image.
- Treating the
godirective, toolchain, and compiler version as the same concept. - Raising a library’s minimum Go version just to use new CI tooling.
- Letting generators and builders depend on host PATH, producing unreproducible output.
- Omitting Go version and platform from cache keys and reusing incompatible caches.
- Rolling back only PATH without restoring
go.mod, images, and supply-chain checksums.
Follow-up questions
Why validate the Go 1.26 bootstrap version separately?
The self-hosting chain uses an older Go to compile the new Go. If the image is below 1.24.6, the upgrade fails during compiler construction regardless of application-source compatibility.
When should a library raise its go directive?
When its source or standard-library API truly requires the newer version, with the minimum written into the release contract. A CI or internal-tool upgrade alone does not change downstream promises.
Does automatic toolchain download solve every version problem?
No. It still needs network access, a trusted proxy, caches, and an offline policy; cgo, platform tools, and bootstrap dependencies must be pinned separately.
How do you prove rollback works?
Actually restore the old image, module directives, toolchain, and caches in a canary, rebuild, and compare tests, artifact hashes, and downstream installation rather than checking version strings only.