Code of the Day
IntermediatePackages & testing

Go modules

Manage dependencies with go.mod and go.sum — go get, go mod tidy, replace directives, and semantic versioning.

GoIntermediate11 min read
Recommended first
By the end of this lesson you will be able to:
  • Read and explain the key fields in a go.mod file
  • Add and update dependencies with go get
  • Run go mod tidy to clean up unused requirements
  • Use a replace directive for local development
  • Apply semantic versioning rules to choose a dependency version

are the standard way to manage dependencies. Every Go project has a go.mod file at its root that declares the module path, the minimum Go version, and the set of required external modules. Understanding modules is essential the moment you pull in any external package.

The go.mod file

Running go mod init creates a go.mod for you:

go mod init github.com/acme/myapp

A minimal go.mod looks like:

module github.com/acme/myapp

go 1.22

require (
    github.com/some/library v1.4.2
)
  • module — the canonical import path for this module. Other modules use this path when they import your code.
  • go — the minimum Go toolchain version required.
  • require — the direct (and sometimes indirect) dependencies with their minimum versions.

go.sum — the lock file

go.sum records the expected cryptographic hashes of every module version you depend on. You never edit it by hand, but you do commit it. The Go toolchain verifies downloads against these hashes, preventing supply-chain tampering.

github.com/some/library v1.4.2 h1:abcdef...
github.com/some/library v1.4.2/go.mod h1:ghijkl...

Commit both go.mod and go.sum to version control. This makes builds reproducible and lets the toolchain detect if a dependency is tampered with after download.

Adding dependencies with go get

# Add or upgrade a dependency
go get github.com/some/library@v1.5.0

# Use the latest released version
go get github.com/some/library@latest

# Download a specific commit (for pre-release work)
go get github.com/some/library@abc1234

go get updates go.mod and go.sum automatically. After go get, run your tests to make sure nothing broke.

go mod tidy

Over time go.mod drifts: you remove imports but the require lines stay. go mod tidy fixes that:

go mod tidy

It adds any missing requirements and removes any that are no longer needed. Run it before committing, especially after refactoring.

Always run go mod tidy before committing dependency changes. A go.mod with stale or missing requirements causes confusing build failures for other contributors.

The replace directive

During local development you sometimes want to use a local version of a dependency — perhaps because you are working on both a library and its consumer at the same time:

replace github.com/acme/mylib => ../mylib

This tells Go to use the local directory ../mylib instead of downloading from the registry. Remove replace before merging to main — it only makes sense on a developer's machine.

Semantic versioning

Go modules follow (semver): vMAJOR.MINOR.PATCH.

  • PATCH bump — backwards-compatible bug fix.
  • MINOR bump — backwards-compatible new feature.
  • MAJOR bump — breaking change. A major version ≥ 2 must be reflected in the module path: github.com/acme/mylib/v2.

Go uses Minimum Version Selection (MVS): if module A requires library v1.2.0 and module B requires v1.4.0, your build uses v1.4.0 (the minimum that satisfies both). This is deterministic and avoids the "latest always wins" problems seen in other ecosystems.

# See which versions are available
go list -m -versions github.com/some/library

# Check why a dependency is included
go mod why github.com/some/library

Working with private modules

For private repositories, set GONOSUMCHECK and GOPRIVATE:

export GOPRIVATE=github.com/mycompany/*

This tells the toolchain to skip the public checksum database and download proxy for your private modules, and instead go directly to the VCS.

Check your understanding

Knowledge check

  1. 1.
    You remove several imports from your code but forget to update go.mod. What command cleans this up?
  2. 2.
    You should commit go.sum to version control.
  3. 3.
    A library releases a breaking change and bumps to v2.0.0. How must callers import the new version?

Do it yourself

Initialise a new module and add an external dependency:

mkdir modtest && cd modtest
go mod init github.com/me/modtest
go get golang.org/x/text@latest
go mod tidy
cat go.mod
cat go.sum | head -4

Where to go next

With modules managing your dependencies, the next lesson covers testing basics — how to write TestXxx functions that give you confidence your code is correct.

Finished reading? Mark it complete to track your progress.

On this page