Question

Go Module Updates: go get -u vs go mod tidy

gomodulepackagego-modules

Question

Given a Go module at a fixed commit, several commands produce different go.mod files:

git clone git://github.com/walles/moar
cd moar
git checkout d24acdbf

On a clean clone:

go get -u

produces a go.mod file with 19 lines.

go get -u
go mod tidy

produces 14 lines, while:

go mod tidy

produces 13 lines.

Deleting all entries in the require block before running either command produces still different results. Why do these approaches produce different dependency requirements, and what is the correct way to update a Go module to the newest compatible versions of its dependencies?

Short Answer

You will learn the separate responsibilities of go get -u and go mod tidy, why Go does not simply select the newest version of every module, and a practical workflow for upgrading dependencies while keeping go.mod and go.sum consistent.

Concept

Go modules track dependencies in two related files:

  • go.mod declares the module's dependency requirements and Go version.
  • go.sum stores checksums for module content used during builds and dependency resolution.

Two commands are often confused because they both can modify these files, but they solve different problems:

  • go get -u changes versions. It upgrades eligible modules in the dependency graph, subject to Go's module version rules.
  • go mod tidy cleans the module definition. It adds requirements needed by the source code and removes requirements no longer needed.

Go uses Minimal Version Selection (MVS). Rather than choosing the newest release of every module blindly, it selects one version of each module: the highest version required anywhere in the complete dependency graph. This gives repeatable builds and avoids unnecessary upgrades.

As a result, an upgrade is not guaranteed to mean “the latest version of every indirect module.” A direct dependency may require a particular version of another module, and MVS selects the highest required version that is actually reachable from the build list.

The exact contents and formatting of go.mod can also vary across Go releases. In particular, modern Go versions may record indirect dependencies differently because of module graph pruning and explicit indirect requirements.

Mental Model

Think of go.mod as a packing list for a trip.

  • go get -u is like replacing items on the list with newer models. It focuses on upgrading versions.
  • go mod tidy is like comparing the packing list with your actual itinerary. It removes items you no longer need and adds items that the itinerary proves you need.
  • MVS is the rule that says: if several people request different versions of the same item, bring the newest version that anyone explicitly requires—not necessarily the newest item sold in the store.

Deleting the require block is like throwing away the packing list and asking Go to reconstruct it from the source code. It may work, but it is an unnecessarily disruptive way to manage normal dependency updates.

Take Quiz

Syntax and Examples

The usual commands are:

# Upgrade dependencies used by packages in this module.
go get -u ./...

# Recalculate required modules and remove unused requirements.
go mod tidy

# Verify that dependencies and checksums are consistent.
go mod verify

# Run the project tests after upgrading.
go test ./...

For a smaller, lower-risk upgrade that only permits patch releases:

go get -u=patch ./...
go mod tidy
go test ./...

To upgrade one dependency deliberately:

go get example.com/library@latest
go mod tidy
go test ./...

For example, if your code imports:

import "github.com/acme/widget"

you can update just that dependency:

go get github.com/acme/widget@latest
go mod tidy

Use ./... when you intend to consider packages throughout the current module. It makes the command's package scope explicit.

Step by Step Execution

Consider this module:

// cmd/report/main.go
package main

import (
    "fmt"

    "example.com/colors"
)

func main() {
    fmt.Println(colors.Blue("ready"))
}

Suppose its go.mod initially contains:

module example.com/report

go 1.22

require example.com/colors v1.2.0

Now run:

go get -u ./...

Step by step:

  1. Go loads packages represented by ./....
  2. It sees that cmd/report imports example.com/colors.
  3. It examines the module graph rooted at the current requirements.
  4. It upgrades eligible modules to newer compatible versions according to -u and MVS.
  5. It writes changed version requirements to go.mod and needed checksums to go.sum.

Next, run:

go mod tidy
  1. Go examines imports in packages in the main module, including relevant test dependencies.
  2. It ensures each needed imported package has a module requirement.
  3. It removes requirements that are no longer needed under the current module graph rules.
  4. It updates go.sum to retain checksums required for reproducible module operations.

Therefore, tidy after an upgrade can make go.mod shorter: the upgrade command may have left requirements that are no longer necessary after the graph is recalculated.

Real World Use Cases

Common dependency-management tasks include:

  • Routine maintenance: Run patch upgrades, test the application, and commit the resulting go.mod and go.sum changes.
  • Security remediation: Upgrade a vulnerable direct dependency to a fixed version, then run go mod tidy and the test suite.
  • Library upgrades: Update one API client, database driver, or framework at a time to make regressions easier to identify.
  • Removing code: After deleting a feature and its imports, run go mod tidy to remove dependencies that the module no longer needs.
  • CI validation: Use go mod tidy in a check that fails when developers forgot to commit required module-file updates.
  • Investigating a version: Use go list -m -versions example.com/module to inspect available versions before choosing an upgrade target.
Take Quiz

Real Codebase Usage

A conservative dependency-update workflow in a production repository is usually:

# Inspect what can change first.
go list -m -u all

# Prefer low-risk patch updates when appropriate.
go get -u=patch ./...

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

For a major or behavior-changing dependency, teams commonly upgrade it separately:

go get github.com/acme/client/v2@v2.4.0
go mod tidy
go test ./...

Useful project patterns include:

  • Small upgrade commits: Keep one dependency or a related set of upgrades per pull request.
  • Tests after every update: Successful module resolution does not prove API compatibility or runtime correctness.
  • Reviewing diffs: Check unexpected major version changes, replaced modules, and large indirect dependency changes.
  • Reproducible builds: Commit both go.mod and go.sum; do not treat go.sum as disposable output.
  • Explicit version pinning when needed: Use a specific version such as @v1.8.3 when an upgrade must be controlled rather than automatically selected.

Common Mistakes

Treating go get -u as a cleanup command

go get -u

This is primarily an upgrade operation. It may not remove every requirement that is no longer necessary.

Avoid it: Follow upgrades with:

go mod tidy

Expecting “latest” to be universally safe

go get -u ./...

A newer minor version can change behavior, deprecate APIs, or expose assumptions in your code. Major versions can require import-path changes, such as /v2.

Avoid it: Review the diff, read release notes for important modules, and run tests.

Deleting the entire require block for normal upgrades

require (
    // deleted manually
)

This can force Go to reconstruct requirements and may create a large, confusing diff. It also obscures the intentional dependency history of the change.

Avoid it: Use go get to change versions and go mod tidy to remove unused requirements. Manual removal is best reserved for deliberate repair or investigation.

Forgetting test-only dependencies

A package used only in _test.go files can still be required by the module.

Avoid it: Run go mod tidy and go test ./..., not only go build for one command.

Editing go.sum to fix dependency problems

Deleting checksum lines may only hide the real issue temporarily and can cause unnecessary downloads or diffs.

Avoid it: Let Go manage checksums through normal commands such as go mod tidy, go get, and go mod download.

Assuming line count indicates correctness

A shorter go.mod is not automatically better. The correct file is the one that accurately represents the dependencies needed by the module under your Go toolchain's rules.

Avoid it: Judge changes by builds, tests, intended versions, and a reviewed diff—not by the number of lines.

Comparisons

Command or actionPrimary purposeChanges versions?Removes unused requirements?Typical use
go get -u ./...Upgrade eligible dependenciesYesNot its main jobRoutine dependency upgrade
go get -u=patch ./...Upgrade only patch versionsYes, patch onlyNot its main jobLower-risk maintenance
go get module@versionSelect a specific dependency versionYesNot its main jobTargeted upgrade or downgrade
go mod tidySynchronize module files with source importsIt can resolve required versions, but is not an “upgrade all” commandYesCleanup after code or dependency changes
go mod downloadDownload modules into the cacheNo intended version selectionNoPrefetching dependencies in automation
Manual require deletionForce requirements to be rediscoveredIndirectlyIndirectlyDebugging or exceptional repair, not routine maintenance

go mod tidy answers: What dependencies does this source code need?

go get -u answers: What newer versions should Go select for the dependencies in scope?

Take Quiz

Cheat Sheet

# See selected modules with available upgrades.
go list -m -u all

# Upgrade dependencies used by all packages in this module.
go get -u ./...

# Only permit patch upgrades.
go get -u=patch ./...

# Upgrade one module to its latest version.
go get example.com/module@latest

# Choose an exact version.
go get example.com/module@v1.2.3

# Add missing requirements and remove unnecessary ones.
go mod tidy

# Test all packages.
go test ./...

# Check downloaded modules against go.sum.
go mod verify

Key rules:

  • Commit both go.mod and go.sum.
  • Run go mod tidy after dependency or import changes.
  • Run tests after any upgrade.
  • MVS selects the highest version required in the graph; it does not blindly install every latest release.
  • Prefer targeted or patch-only upgrades when reducing upgrade risk matters.
  • Module-file details can differ between Go toolchain versions.

FAQ

Why does go get -u change more modules than the one I care about?

Dependencies have their own dependencies. Updating one selected module can change the version requirements in the overall module graph.

Does go mod tidy update every dependency to the latest version?

No. Its main purpose is to make go.mod match the imports used by the module. Use go get -u ./... when you explicitly want upgrades, then run go mod tidy.

What is the right command to update all Go dependencies?

A practical command sequence is:

go get -u ./...
go mod tidy
go test ./...

Use go get -u=patch ./... when you want patch-only upgrades.

Why does go.mod contain indirect dependencies?

Some indirect requirements are recorded to make version selection reproducible and to support Go's module graph rules. The exact representation can differ by Go version.

Should I delete unused dependencies manually from go.mod?

Usually no. Run go mod tidy, which calculates what is needed from your source code and module graph.

Why is my go.sum still large after go mod tidy?

go.sum can contain checksums needed for reproducible resolution of modules and their go.mod files. Its size is not a reliable sign of a problem.

Can dependency upgrades break a Go program even when it compiles?

Yes. A dependency may change runtime behavior, defaults, protocol behavior, or edge-case handling. Run unit, integration, and relevant end-to-end tests.

Why do I get different results with another Go version?

The Go toolchain evolves, including how it records module requirements and prunes dependency graphs. Use a pinned Go version in CI and teams when reproducibility matters.

Take Quiz

Mini Project

Description

Create a repeatable dependency-update script for a Go module. The script performs patch-level upgrades, tidies the module files, verifies downloaded modules, and runs the test suite. This reflects a cautious workflow often used in application repositories.

Goal

Produce a shell script that safely performs a patch dependency update and stops if any verification or test step fails.

Requirements

Use go get -u=patch ./... to request patch-level updates. Run go mod tidy after the upgrade. Run go mod verify before testing. Run go test ./.... Print a success message only after every command succeeds.

Take Quiz

Keep learning

Related questions

View more