Creating and publishing a module¶
A module is a collection of packages versioned together — the unit Go
uses to distribute and depend on code. It's defined by a go.mod file at
its root, which names the module and records its dependencies. One module
typically maps to one repository.
Starting a module¶
go mod init creates go.mod. The argument is the module path — the
import prefix for every package inside, and (for published modules) the
place it can be downloaded from.
The module line sets the import prefix: a package in store/ is now
imported as example.com/shop/store. The go line records the language
version the module targets.
From Python:
go.modis the rough equivalent ofpyproject.toml— it names the project and pins dependencies. There's no virtualenv: the module graph is resolved per-build and dependencies are cached globally.
Adding a dependency¶
Import a package and let the tooling fetch it. go get adds it; go mod
tidy reconciles go.mod/go.sum with what the code actually imports.
This records the dependency and its exact version in go.mod, and writes
cryptographic checksums to go.sum so future builds verify they got
the identical bytes.
go.sum is not a lockfile in the npm sense — go.mod already pins exact
versions; go.sum is the integrity record. Commit both.
Semantic versioning and the module cache¶
Go modules use semantic versioning (vMAJOR.MINOR.PATCH). Version
selection is Minimal Version Selection: a build uses the lowest
version that satisfies all requirements — builds are reproducible without
a separate lockfile. Downloaded modules live in a global, read-only cache
shared across projects.
Upgrades are explicit:
go get github.com/google/uuid@latest # newest
go get github.com/google/uuid@v1.6.0 # a specific version
go get github.com/google/uuid@none # remove
Major versions: the /vN rule¶
A breaking change means a new major version, and from v2 onward the
major version becomes part of the module path:
Importers then write example.com/shop/v2/store. This lets v1 and v2
coexist in one build — a different answer to dependency conflicts than
Python's single-version-per-environment model.
Publishing¶
There is no central registry to upload to. Publishing a module is just pushing a tagged commit to a public repository whose URL matches the module path.
The first time someone runs go get example.com/shop@v1.0.0, the Go
tooling fetches it straight from the repo (often via the module proxy) and
records the checksum. The module path must match the repo location so the
tooling knows where to look.
Replacing a dependency (local or forked)¶
The replace directive swaps a dependency for another version, a fork, or
a local path — invaluable while developing two modules side by side:
// go.mod
require example.com/lib v1.2.0
replace example.com/lib => ../lib // use local checkout instead
replace is build-local: it affects this module's builds only, not
anyone who depends on you.
Quick reference¶
| Command / directive | Purpose |
|---|---|
go mod init <path> |
create go.mod, set the module path |
go get <pkg>@<ver> |
add / upgrade / pin a dependency |
go get <pkg>@none |
remove a dependency |
go mod tidy |
sync go.mod/go.sum with imports |
go.sum |
checksums for integrity (commit it) |
require |
a dependency and its version |
replace A => B |
swap a dependency (local/fork) |
/vN in module path |
major versions ≥ 2 |
git tag vX.Y.Z + push |
publish a version |