Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
A Go module is the unit of versioned dependency management — one go.mod per repo, identified by an import path that's typically your repo URL. The go.mod file pins your direct dependencies; go.sum cryptographically pins the full transitive graph so nobody can swap a tagged version without you noticing. If you don't understand go.mod line by line, you'll be confused the first time go mod tidy rewrites your file or a replace directive saves you from a broken upstream.
go mod init creates go.mod, which declares the module path and the minimum Go version your code requires. Adding a dependency with go get (or just importing it and running go mod tidy) records the exact version in go.mod and writes a checksum to go.sum. The module graph is a directed graph of all your dependencies and their dependencies — go mod graph prints it. Crucially, the selected version is always the minimum compatible version that satisfies all requirements in the graph, not the latest. This is MVS (Minimum Version Selection): deterministic and reproducible by design, no lock file needed beyond what go.mod already is.
$ mkdir hello && cd hello
$ go mod init github.com/you/hello
$ cat > main.go <<'EOF'
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
fmt.Println("hello", uuid.NewString())
}
EOF
$ go mod tidy
go: finding module for package github.com/google/uuid
go: found github.com/google/uuid in github.com/google/uuid v1.6.0
$ cat go.mod
module github.com/you/hello
go 1.22
require github.com/google/uuid v1.6.0go run main.gogo mod init github.com/you/<name> in a new dir. Open go.mod and read all 3 lines.go get github.com/google/uuid@latest then go mod tidy. Watch go.sum populate.go.sum — note that every transitive dep gets two hashes: one for the module zip, one for go.mod. This is the supply-chain defense.go mod graph and go mod why github.com/google/uuid — these tell you what depends on what and why something is in your tree.Use these three in order. Each builds on the one before.
In one paragraph, explain what a Go module is and how it differs from a 'package' — and why the module path conventionally matches the repo URL.
Walk me through how `go.sum` actually defends against supply-chain attacks. What does `GONOSUMCHECK` do and when would you use it?
I'm forking an upstream module and need to test my fork locally before publishing. Compare `replace` directives, `go work`, and `go mod edit -replace` — which fits which workflow?