Dependency Management
Overview
Go modules, introduced in Go 1.11 and default since Go 1.16, provide built-in dependency management. This chapter covers everything about go.mod, go.sum, versioning, and managing external libraries.
Understanding Go Modules
A module is a collection of packages with a go.mod file that defines: - Module path (import path) - Go version - Dependencies and their versions
Creating a Module
$ mkdir myproject && cd myproject
$ go mod init github.com/username/myprojectThis creates go.mod:
module github.com/username/myproject
go 1.26
The go.mod File
Anatomy of go.mod
module github.com/username/myproject
go 1.26
require (
github.com/gin-gonic/gin v1.9.1
github.com/stretchr/testify v1.8.4
)
require (
// indirect dependencies
github.com/bytedance/sonic v1.9.1 // indirect
golang.org/x/net v0.10.0 // indirect
)
exclude (
github.com/broken/pkg v1.0.0
)
replace (
github.com/old/module => github.com/new/module v2.0.0
github.com/local/dev => ../local-dev
)
retract (
v1.0.0 // Contains security vulnerability
[v1.1.0, v1.2.0] // Accidental release
)
Directives Explained
| Directive | Purpose |
|---|---|
module |
Module path (required) |
go |
Minimum Go version |
require |
Dependencies with versions |
exclude |
Versions to ignore |
replace |
Substitute modules |
retract |
Mark versions as broken |
The go.sum File
go.sum contains cryptographic checksums representing module dependencies:
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tn+7r7IyQ4PGNmP1c42GGpvQ=
Each entry has: - Module path and version - Hash of module contents (h1:) - Hash of go.mod file
Never edit go.sum manually — it’s managed by Go tools.
Adding Dependencies
Using go get
# Add latest version
$ go get github.com/gin-gonic/gin
# Add specific version
$ go get github.com/gin-gonic/gin@v1.9.1
# Add latest minor version
$ go get github.com/gin-gonic/gin@v1.9
# Add specific commit
$ go get github.com/gin-gonic/gin@a1b2c3d
# Add from branch
$ go get github.com/gin-gonic/gin@mainAutomatic Detection
Simply import and run:
import "github.com/gin-gonic/gin"$ go mod tidy # Downloads and records dependencyUpdating Dependencies
# Update specific package (latest minor/patch)
$ go get -u github.com/gin-gonic/gin
# Update to specific version
$ go get github.com/gin-gonic/gin@v1.10.0
# Update all dependencies
$ go get -u ./...
# Update only patch versions (safer)
$ go get -u=patch ./...Semantic Versioning
Go modules expect Semantic Versioning:
v1.2.3
│ │ │
│ │ └── Patch: bug fixes (backward compatible)
│ └──── Minor: new features (backward compatible)
└────── Major: breaking changes
Major Version Suffixes
For v2+, the module path includes the major version:
// go.mod
require github.com/user/project/v2 v2.1.0
// import
import "github.com/user/project/v2"Managing go.mod
go mod tidy
The most important maintenance command:
$ go mod tidyThis: - Adds missing dependencies - Removes unused dependencies - Updates go.sum
Run this frequently, especially before commits.
go mod download
Pre-download all dependencies:
$ go mod downloadUseful for CI caching or air-gapped environments.
go mod verify
Check that dependencies haven’t been modified:
$ go mod verify
all modules verifiedgo mod why
Explain why a dependency is needed:
$ go mod why golang.org/x/net
# github.com/username/myproject
github.com/username/myproject
github.com/gin-gonic/gin
golang.org/x/net/htmlgo mod graph
Show dependency graph:
$ go mod graph
github.com/username/myproject github.com/gin-gonic/gin@v1.9.1
github.com/gin-gonic/gin@v1.9.1 github.com/bytedance/sonic@v1.9.1
...Vendoring
Copy dependencies into your repository:
# Create vendor directory
$ go mod vendor
# Build using vendor
$ go build -mod=vendorVendoring pros: - Reproducible builds - Works offline - Audit dependencies
Vendoring cons: - Bloated repository - Manual updates
Replace Directive
Local Development
Replace a module with a local path:
replace github.com/some/package => ../local-package
Fork Substitution
Use your fork instead of original:
replace github.com/original/pkg => github.com/yourfork/pkg v1.2.3
Version Override
Force a specific version:
replace github.com/vulnerable/pkg v1.0.0 => github.com/vulnerable/pkg v1.0.1
Private Modules
GOPRIVATE
Configure private repository patterns:
$ go env -w GOPRIVATE=github.com/mycompany/*,gitlab.internal.com/*Git Credentials
For private repos, configure Git:
# Use SSH
$ git config --global url."git@github.com:".insteadOf "https://github.com/"
# Or use access token
$ git config --global url."https://token:${TOKEN}@github.com/".insteadOf "https://github.com/"Dependency Management Best Practices
1. Pin Dependencies
Always use exact versions in production:
# Good: exact version
$ go get github.com/pkg/errors@v0.9.1
# Risky: floating version
$ go get github.com/pkg/errors@latest2. Regular Updates
Check for updates periodically:
$ go list -m -u all3. Minimal Dependencies
Fewer dependencies = fewer problems: - Check if you really need that package - Consider stdlib alternatives - Watch for transitive dependencies
4. Security Scanning
Use vulnerability scanning:
# Built-in (Go 1.18+)
$ go install golang.org/x/vuln/cmd/govulncheck@latest
$ govulncheck ./...Common Issues
Module Not Found
# Ensure GOPROXY is set
$ go env GOPROXY
https://proxy.golang.org,direct
# Try direct fetch
$ GOPROXY=direct go get github.com/some/packageVersion Conflicts
# See what's required
$ go mod graph | grep conflicting-package
# Force specific version
$ go get conflicting-package@v1.2.3Checksum Mismatch
# Clear cache and retry
$ go clean -modcache
$ go mod downloadSummary
| Command | Purpose |
|---|---|
go mod init |
Create new module |
go mod tidy |
Sync dependencies |
go get pkg@version |
Add/update dependency |
go mod download |
Pre-fetch dependencies |
go mod verify |
Verify checksums |
go mod vendor |
Create vendor directory |