Go¶
Go's module system is refreshingly simple compared to other ecosystems. This appendix covers Go's approach to dependency management.
Refreshingly Boring
Go's dependency management is refreshingly boring. I mean that as praise. One tool (go mod). One lock file (go.sum). Checksums verified against a public database. Minimal version selection that's actually deterministic. After years of npm and pip chaos, Go feels like someone thought through the problem before shipping a solution. It's not perfect, but it's remarkably hard to shoot yourself in the foot.
Go Modules¶
Since Go 1.11, modules are the standard for dependency management. A module is a collection of packages with a go.mod file.
Module Basics¶
// go.mod
module github.com/you/project
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/lib/pq v1.10.9
)
Adding Dependencies¶
Dependencies are added automatically when you import them:
Or explicitly:
The Two Lock Files¶
go.mod: What you depend on (like package.json).
go.sum: Cryptographic hashes of dependencies (like package-lock.json).
Always commit both files.
Version Selection¶
Go uses Minimal Version Selection (MVS): if multiple dependencies require the same module, Go selects the minimum version that satisfies all requirements.
Your project requires:
- A requires X >= 1.0
- B requires X >= 1.2
Go selects X 1.2 (minimum that satisfies both)
This is deterministic and avoids the "I got a different version" problem.
Version Specifiers¶
# Latest version
go get github.com/pkg/errors@latest
# Specific version
go get github.com/pkg/errors@v0.9.1
# Specific commit
go get github.com/pkg/errors@abc123
# Branch (usually avoid)
go get github.com/pkg/errors@main
Updating Dependencies¶
# Update all dependencies
go get -u ./...
# Update specific dependency
go get -u github.com/gin-gonic/gin
# Update to specific version
go get github.com/gin-gonic/gin@v1.9.1
# Update only patch versions
go get -u=patch ./...
Security¶
Checksum Database¶
Go verifies downloads against a checksum database (sum.golang.org):
# go.sum contains hashes
github.com/gin-gonic/gin v1.9.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
If someone compromises a module, the hash won't match. The download fails.
Vulnerability Scanning¶
# Built-in vulnerability scanner (Go 1.18+)
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
Output shows: - Which vulnerabilities affect your code - Whether vulnerable code paths are actually reachable
Private Modules¶
For private repositories:
# Set GOPRIVATE
go env -w GOPRIVATE=github.com/yourorg/*
# Or in environment
export GOPRIVATE=github.com/yourorg/*
This skips the checksum database for private modules (it can't verify them anyway).
Dependency Management¶
Viewing Dependencies¶
# List all dependencies
go list -m all
# Why is this dependency here?
go mod why -m github.com/some/dependency
# Dependency graph
go mod graph
Cleaning Up¶
# Remove unused dependencies, add missing ones
go mod tidy
# Verify dependencies match go.sum
go mod verify
Vendoring¶
Go supports vendoring (copying dependencies into your repo):
When to vendor:
- Reproducibility requirements (can build offline)
- Compliance requirements (audit all code)
- CI performance (no downloads)
When not to:
- Most projects—module system is sufficient
- Rapid development (vendor sync overhead)
Module Proxies¶
The Proxy Chain¶
By default, Go downloads through proxy.golang.org:
Benefits: - Faster downloads (cached) - Immutability (once published, can't change) - Availability (survives if source goes down)
Private Proxies¶
For organizations:
# Use private proxy for your org
export GOPROXY="https://proxy.yourorg.com,https://proxy.golang.org,direct"
export GONOSUMDB="github.com/yourorg/*"
Direct Downloads¶
Use for private modules or when the proxy is unavailable.
Common Patterns¶
Replacing Dependencies¶
For forks or local development:
// go.mod
replace github.com/original/module => github.com/yourfork/module v1.0.0
// Or local path
replace github.com/original/module => ../local-module
Remove replacements before publishing.
Excluding Versions¶
Block specific versions:
Retracting Versions¶
If you maintain a module and release a bad version:
// go.mod (of your module)
retract (
v1.0.0 // Security vulnerability
[v1.1.0, v1.1.5] // Broken range
)
Building and Compilation¶
Reproducible Builds¶
Go builds are reproducible by default:
The -trimpath flag removes machine-specific paths.
Static Binaries¶
Go produces static binaries by default (usually):
No runtime dependencies. The binary is the deployment artifact.
Cross-Compilation¶
# Build for Linux from macOS
GOOS=linux GOARCH=amd64 go build -o app-linux .
# Build for Windows
GOOS=windows GOARCH=amd64 go build -o app.exe .
CI Integration¶
# GitHub Actions
name: Go
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Download dependencies
run: go mod download
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
- name: Vulnerability scan
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
Best Practices Checklist¶
-
go.modandgo.sumcommitted - Run
go mod tidybefore commits -
govulncheckin CI pipeline - GOPRIVATE set for private repos
- No
replacedirectives in published modules - Pin Go version in CI
Quick Reference¶
Essential Commands¶
go mod init module-name # Initialize module
go mod tidy # Clean up dependencies
go get package@version # Add/update dependency
go mod verify # Verify checksums
govulncheck ./... # Vulnerability scan
go mod graph # View dependency graph
go mod why -m package # Why is this a dependency?
Version Selectors¶
| Selector | Meaning |
|---|---|
@latest | Latest release |
@v1.2.3 | Specific version |
@abc123 | Specific commit |
@main | Branch tip (avoid) |
Environment Variables¶
| Variable | Purpose |
|---|---|
GOPROXY | Module proxy URL |
GOPRIVATE | Private module patterns |
GONOSUMDB | Skip checksum DB for these |
GOFLAGS | Default flags for go commands |