Modules, MVS, and Toolchain
Modules, MVS, and Toolchain
Overview
Go modules are a versioned dependency graph with a precise selection algorithm: Minimal Version Selection (MVS). Treat go.mod / go.sum as production config — not editor noise.
Basics: Dependency management, Workspaces.
Diagram: MVS selection
flow:
[Max]
|
v
[Build]
Core Files
| File | Role |
|---|---|
go.mod |
Module path, Go version, requires, replaces, excludes, toolchain |
go.sum |
Cryptographic checksums of module contents |
vendor/ |
Optional frozen source trees |
go mod tidy # align requires with imports
go mod download
go mod verify
go mod graphMinimal Version Selection
Unlike many package managers that pull “newest compatible,” MVS picks the minimum version that satisfies all constraints in the build list.
Your module requires A v1.2
B requires A v1.5
C requires A v1.3
----------------------------
Selected A = v1.5 (max of minima)
Properties:
- Reproducible given the same
go.modbuild list - Not “always latest”
- Upgrades are explicit (
go get pkg@vX)
go list -m all
go list -m -versions github.com/some/dep
go get example.com/dep@v1.4.2
go get example.com/dep@none # dropThe Build List
go list -m all shows the selected set. Understanding diamond dependencies is mostly reading that list and go mod graph | grep dep.
replace and exclude
replace example.com/fork => ../fork
exclude example.com/bad v1.0.0replace is powerful for local development; avoid permanent replaces in published modules — consumers inherit pain. Prefer releasing fixed versions.
Retracts
Module authors can retract broken versions so go get avoids them. If you cannot upgrade, pin carefully and track advisories.
Toolchain Directive
Modern Go can select a toolchain version:
go 1.27
toolchain go1.27.1go env GOTOOLCHAIN # auto | local | path | versionCI should pin or intentionally use auto with care so builds do not silently jump compilers mid-incident.
Vendoring
go mod vendor
go build -mod=vendorPros: offline builds, airgapped, reviewable third-party diffs. Cons: repo size, merge noise. Good for enterprises and release branches.
Checksums and Proxies
GOPROXY (default proxy.golang.org)
GOSUMDB (sum.golang.org)
GOPRIVATE / GONOSUMDB for private modules
export GOPRIVATE=github.com/myorg/*Private modules need auth to the host; sumdb is skipped for GOPRIVATE.
Diagnosis Playbook
| Symptom | Action |
|---|---|
| “missing go.sum entry” | go mod tidy |
| Unexpected old dep | go mod graph + who requires it |
| Local replace forgotten | Search replace in go.mod |
| CI != laptop | Print go version, go env, go list -m all |
| Ambiguous import | Module path vs directory mismatch |
Experiment
mkdir /tmp/mvs-demo && cd /tmp/mvs-demo
go mod init example.com/demo
go get rsc.io/quote@v1.5.2
go list -m all
go mod graph | head
cat go.mod
go mod tidyWhat to notice: go.mod records the selected versions; go.sum grows with hashes; graph shows edges that forced versions upward.
Try next: Introduce a replace to a local stub module and build; then remove replace and fetch the real version.