Module lessons (4/5)
Go modules
A Go module is a versionable unit made up of one or more packages. It is the foundation of the modern dependency system (Go 1.11+): no more GOPATH, no more mandatory vendor, explicit and reproducible dependencies.
go.mod: the module manifest
module example.com/myapp
go 1.22
require (
github.com/google/uuid v1.6.0
golang.org/x/time v0.5.0
)
Three main directives:
module <path>— the module's import path. All internal packages are imported asexample.com/myapp/sub/pkg. It does not have to be a real URL, but to publish viago getit must resolve.go <version>— the minimum Go version required (also used to control which language features are allowed, e.g. range-over-func in 1.23).require— the direct dependencies with their exact version.
Other directives: replace (substitutes a dep with a fork or a local path), exclude, retract.
go.sum: the cryptographic lockfile
Alongside go.mod, go.sum contains a cryptographic hash of EVERY downloaded version (including transitive dependencies). It guarantees reproducibility: if someone tampers with a published version, the build fails.
Always commit it. It is updated automatically by go commands.
Essential commands
go mod init example.com/myapp # create go.mod
go get github.com/google/uuid # add the dep at its latest version
go get github.com/google/uuid@v1.5.0 # specific version
go get -u ./... # upgrade every dep
go mod tidy # remove unused deps, add missing ones
go mod download # download everything into the cache
go mod why github.com/x/y # explain why a dep is included
go mod graph # full dependency graphgo mod tidy is the one you run after every change to imports: it guarantees that go.mod reflects exactly the packages actually used.
Semantic Import Versioning (SIV)
Versions follow SemVer: v1.2.3 = MAJOR.MINOR.PATCH. The interesting part is major v2+:
- Major
v0andv1→ normal path:github.com/foo/bar. - Major
v2or later → the path includes the major version:github.com/foo/bar/v2.
import "github.com/foo/bar/v2"This lets you import both v1 and v2 of the same library at the same time (useful during progressive migrations).
Import paths and internal packages
If your module is example.com/myapp, a utils/ sub-folder is imported as:
import "example.com/myapp/utils"The path depends only on the name declared in go.mod, not on the location on disk: the same code in a different folder keeps working.
Workspace mode (Go 1.18+)
To work on multiple modules that reference each other at the same time (e.g. a library and an app that uses it):
go work init ./lib ./appThis creates go.work: go build will use the local version of ./lib instead of downloading the published one. Do not commit go.work (it is a development environment).
Exercises
The module is example.com/myapp and contains a utils sub-folder. Import the sub-package in main using the full path.
Show hint
The import path is the module name (from go.mod) plus the folder's relative path.
Solution available after 3 attempts
Turn the individual imports into an import group with the import ( ... ) syntax, importing both fmt and os.
Solution available after 3 attempts
Which command removes dependencies no longer imported from go.mod and adds the missing ones?
$ go mod ???