Managing Dependencies

Purpose and Scope

This page explains the everyday workflow for adding, upgrading, downgrading, pruning, and tidying module dependencies in a Go project. A dependency is an external module that provides one or more packages imported by your code. A module is the unit of dependency tracking: it is identified by a module path, records requirements in go.mod, and may have checksums recorded in go.sum. The intended reader is writing application or library code and wants to keep dependency metadata consistent while using the standard Go distribution.

The most important rule is to let the go command update module metadata whenever possible. Go’s public documentation describes dependency management as a workflow built around importing packages, adding code to a module, adding external packages as dependencies, and then changing versions as needed. That workflow keeps requirements consistent and keeps go.mod valid. The Go repository README establishes the distribution context for that workflow: Go is distributed as an open source programming language, official binary releases are published on go.dev, and the canonical source repository is go.googlesource.com/go with a GitHub mirror. Sources: README.md

Dependency management is separate from building the Go toolchain itself. The repository contains command implementations such as the assembler, cgo, compiler front end, and small tools used by profiling workflows. Those commands are part of the Go distribution; your project dependencies, by contrast, are modules named in your own module files. Keeping that distinction clear helps avoid a common confusion: installing Go gives you the go command and bundled tools, while adding a dependency changes your project’s module graph. Sources: src/cmd/addr2line/main.go, src/cmd/asm/main.go, src/cmd/cgo/main.go, src/cmd/compile/internal/gc/main.go

Relevant Source Files

  • README.md - Identifies the Go project, the canonical and mirrored repositories, the BSD-style licensing context, and the official download and installation entry points that put the go command on a developer machine.
  • misc/go_android_exec/main.go - Shows an execution wrapper that can be selected by the Go tool for Android targets, illustrating that build and test execution environments are configured separately from module dependency requirements.
  • src/cmd/addr2line/main.go - Implements a minimal go tool addr2line-style command used by pprof, showing how bundled tools have command-line entry points and usage contracts distinct from module dependencies.
  • src/cmd/asm/main.go - Implements the assembler command entry point, including flag parsing, architecture selection, object creation, and telemetry counting for invocations.
  • src/cmd/cgo/main.go - Defines cgo’s package and file data structures, including Go files, generated C files, C references, and linker flags, which matter when a module uses cgo but are not module-version requirements by themselves.
  • src/cmd/compile/internal/gc/main.go - Defines the compiler Main flow that parses flags and Go source files, type-checks a package, compiles functions, and writes compiled package data.

Core Primitives

A package is the source-level unit you import from Go code. A module is the versioned delivery unit that provides packages. When you write an import for a package outside the standard library and outside your main module, the go command resolves that import to a module requirement. Over time, the module graph can grow because dependencies may have their own dependencies. Your direct requirements are modules your code imports directly or that you explicitly require; indirect requirements are needed by other modules in the graph.

The go.mod file is the primary source of truth for the module path, the Go language version line, and required module versions. The go.sum file records cryptographic checksums used to verify downloaded module content. You generally should not treat go.sum as a hand-written manifest. It is updated as the go command downloads, verifies, and prunes modules. If your editor or IDE is module-aware, it can help run the same operations that the command line runs rather than inventing a separate dependency state.

The local module cache is where downloaded modules are stored for reuse. The module cache is not your repository’s dependency directory, and it should not be confused with vendored source. The normal workflow uses the cache and records version intent in go.mod; optional vendoring copies dependency source into a vendor directory for builds that intentionally use vendored content. For most projects, starting with module-aware commands and committing go.mod and go.sum is the right baseline.

The standard distribution contains many commands, but only the go command owns module metadata workflows. Repository files such as src/cmd/asm/main.go and src/cmd/compile/internal/gc/main.go show tools parsing flags, selecting architecture or compiler state, and writing outputs. Those tool commands participate in building and analyzing Go programs, but they do not replace go get, go mod tidy, or go list for dependency graph maintenance. Sources: src/cmd/asm/main.go, src/cmd/compile/internal/gc/main.go

Workflow for Adding and Changing Dependencies

Start by making sure the project is in a module. If there is no go.mod at the module root, initialize one with a module path that identifies your code. For code you do not plan to publish, the path can still be meaningful inside your organization. For code you plan to publish, the path should match the import path users will write. After initialization, import the packages you need in source files. The dependency workflow begins from real imports rather than from a separate package list.

go mod init example.com/my/app
# edit .go files and add imports
go test ./...

When an imported package is not already provided by the standard library or your main module, add it with the go command. In modern module workflows, go get changes module requirements. You can ask for the latest version, a specific semantic version, a branch or revision supported by the module source, or a downgrade. The command edits go.mod and may update go.sum. Running tests after each meaningful change is the simplest way to confirm that the selected versions still build together.

go get example.com/other/module@latest
go get example.com/other/module@v1.2.3
go get example.com/other/module@v1.1.0
go test ./...

Use go mod tidy after imports change, after deleting packages, or after a larger dependency update. Tidying adds missing requirements needed to build the module’s packages and tests, and removes requirements that are no longer needed. It also updates checksums to match the packages and module versions still reachable from the graph. Treat tidy as the cleanup pass that reconciles source imports, tests, and metadata rather than as a blind formatting command.

go mod tidy
git diff -- go.mod go.sum
go test ./...

To inspect dependency state, use listing and explanation commands before making large changes. go list -m all reports the selected module versions in the build list. go list -m -versions path shows known versions for a module. go mod why -m path explains why a module is needed from the perspective of packages in your module. These commands help distinguish a direct dependency you should manage intentionally from a transitive dependency selected by the module graph.

go list -m all
go list -m -versions example.com/other/module
go mod why -m example.com/other/module

System-to-Code Mapping

The Go repository’s root README is the best source in the supplied evidence for the distribution boundary: users obtain official binaries from go.dev or install from source, then use the installed toolchain to manage application code. That means dependency management starts after installation, in a user module, not by modifying the Go source tree. The same README points contributors to contribution guidelines and separates bug reports, proposals, and questions, reinforcing that user dependency questions belong in the public Go documentation and support channels rather than in toolchain source changes. Sources: README.md

The command sources illustrate what a bundled tool entry point looks like. src/cmd/addr2line/main.go documents a usage form, parses command-line arguments, opens an object file, and translates program counters to file and line output for pprof. src/cmd/asm/main.go parses assembler flags, initializes an architecture, reads assembly inputs, and writes an object file. These commands are shipped with Go and may be invoked directly or through go tool, but their inputs are binaries or assembly files, not go.mod requirements. Sources: src/cmd/addr2line/main.go, src/cmd/asm/main.go

Cgo is a useful boundary case for dependency thinking. src/cmd/cgo/main.go models a package with Go files, generated C files, C compiler options, linker flags, references to C names, and exported functions callable from C. A module can certainly contain packages that use cgo, and such packages may require system libraries or compiler configuration. Those requirements are environmental and build-related; they are not the same as module version requirements recorded by go.mod. Sources: src/cmd/cgo/main.go

Target execution wrappers are another boundary. misc/go_android_exec/main.go can be used by the Go tool as go_android_GOARCH_exec, serializes adb commands, waits for an Android device, copies GOROOT to the device, and prepares temporary directories for test binaries. This kind of file explains how the toolchain executes built artifacts under a specific environment. It is relevant when testing dependency changes on Android, but it does not change how module versions are selected. Sources: misc/go_android_exec/main.go

Compact Command Reference

TaskCommandWhat to check
Initialize dependency trackinggo mod init example.com/my/moduleCreates go.mod at the module root.
Add or upgrade a dependencygo get module/path@latestReview changed require lines and go.sum.
Select a specific versiongo get module/path@v1.2.3Confirm compatibility with go test ./....
Downgrade a dependencygo get module/path@v1.1.0Watch for API breakage in compile and test output.
Remove unused requirementsgo mod tidyReview removed require lines and checksum changes.
See selected modulesgo list -m allInspect the build list chosen by minimal version selection.
Find available versionsgo list -m -versions module/pathChoose a version intentionally.
Explain why a module is presentgo mod why -m module/pathDecide whether it is direct, test-only, or transitive.

Practical Review Checklist

Before committing dependency changes, review both source imports and module files together. A dependency update that only changes go.mod but never updates imports may be accidental. A source import added without a corresponding tidy pass may leave metadata incomplete. Run package tests with go test ./..., inspect the diff to go.mod and go.sum, and record why major upgrades or downgrades were necessary. If the module is published for others, also consider whether the change affects your public API or minimum supported Go version.

When builds fail after a dependency change, first separate module selection problems from toolchain or environment problems. Unknown packages, missing versions, and unexpected upgrades usually point to module metadata or import paths. Compiler diagnostics point to source compatibility. Linker or C compiler failures in cgo packages point to environment configuration. Platform execution failures, such as a device-specific test runner, point to execution setup. The repository’s command entry points show these layers as separate programs and phases, which is a useful mental model when debugging. Sources: src/cmd/cgo/main.go, src/cmd/compile/internal/gc/main.go, misc/go_android_exec/main.go

Next, read the go.mod reference for directive syntax, the modules overview for graph concepts, and the go command reference for command-specific flags and help text. For publishing decisions, switch from dependency management to module source and release workflow guidance, because publishing adds repository layout, tags, semantic import versioning, and compatibility promises that ordinary application dependency updates do not require.