> series: learn go from 0 to 100 | article 15 of 19
Fourteen articles typing go mod init like a ritual without explaining the spell. Today’s the day: what a module is, what go.mod and go.sum hold, and why Go modules are one of the few things in this world that just work.
What a module is
A module is a collection of packages versioned as a unit, defined by a go.mod file at its root (official modules reference). Modules are the standard since Go 1.16 (release notes), burying the GOPATH era, which veterans remember the way people remember Nintendo64.
module github.com/guaduastudio/tareas
go 1.26
require (
github.com/google/uuid v1.6.0
rsc.io/quote v1.5.2
)
The module path is its identity and download address at once: whoever imports github.com/guaduastudio/tasks/store knows where the code lives. The go directive marks the expected language version and require lists direct dependencies with versions.
Day to day: four commands
The whole workflow fits on a napkin (managing dependencies and Using Go Modules):
go mod init github.com/guadua/tasks # creates go.mod
go get rsc.io/quote # adds a dependency
go get rsc.io/quote@v1.5.1 # specific version
go mod tidy # cleanup: adds what's used, drops the dead
go list -m all # lists the full graph
The cultural trick: in Go the import goes into the code first and go mod tidy reconciles afterwards. You write import “rsc.io/quote”, run tidy, and it downloads, records and cleans. Compared to the pip freeze / requirements.txt dance or package.json + lock + node_modules, here there’s ONE real file and zero giant folders. Dependencies live in a shared global cache, not inside each project.

go.sum and the proxy: security by default
Next to go.mod appears go.sum: cryptographic hashes of every dependency. If someone alters the code of an already published version, verification fails and the build stops (modules reference). Downloads go by default through proxy.golang.org, which caches modules forever. Even if an author rage-deletes their repo, your build survives. Remember npm’s left-pad incident? That apocalypse carries an umbrella here.
Both files ALWAYS go into version control, that’s non-negotiable for reproducible Go modules. go.sum is not generated junk, it’s your chain of custody.
Semantic versioning and the v2 rule
Go requires semantic versioning in tags: v1.2.3 means major.minor.patch. Within a major version, the tool picks the MINIMUM version satisfying all requirements (minimal version selection), not the latest available: reproducible builds without a separate lock file (modules reference).
And the rule that throws everyone: from v2 onward, the major version becomes part of the import PATH (github.com/lib/thing/v2). Sounds weird until you think it through: two incompatible major versions can coexist in one binary because, import-wise, they’re different packages.
Weekly challenge: put Go modules to work
- Create a fresh module, import rsc.io/quote (the example module from the official tutorial) and print a quote. Run go mod tidy and inspect what landed in go.mod and go.sum.
- Pin an older version with go get rsc.io/quote@v1.5.1, check the go.mod diff, then upgrade back.
- Run go list -m all and sketch your module’s transitive dependency tree. How many did a single direct dependency drag in?
- Bonus: delete one line from go.sum on purpose and run go build. Read the error calmly: that’s security doing its job.
Go modules: level complete.
Next article: testing, benchmarks and the table-driven tests that make testing in Go almost addictive.
> exit