Preview and Apply Go API Fixes with go fix
go fix rewrites source code to match newer APIs, and running it against a dirty tree is how a "quick modernisation" turns into an unreviewable diff. This guide finds the changes your installed toolchain proposes, reviews them as a patch, and only applies them once the package still builds and the diff makes sense. The examples use Go 1.26.1 from /home/linuxbrew/.linuxbrew/bin/go. The Debian golang-go package installed here is version 2:1.22~2build1, but its manual page documents an older interface, so check the actual go binary on your PATH before relying on a flag.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes for a small module, plus review time for a large repository. You need a Go module, a clean working tree, and a shell. The commands below do not need sudo.
Warning
Do not run this against a valuable tree until you have a commit or another verified backup. go fix changes source files, and the operation has no built-in undo command.
1. Check which Go tool you will run
The command name is not enough when several Go installations are present. Check its path, version, and current help:
$ command -v go
/home/linuxbrew/.linuxbrew/bin/go
$ go version
go version go1.26.1 linux/amd64
$ go help fix
On current Go, the documented shape is go fix [build flags] [-fixtool prog] [fix flags] [packages]. The local go-fix(1) page instead describes a -fix list passed to go tool fix -r. That difference matters: do not copy flags from the manpage blindly when the binary reports a newer command contract. Treat go help fix as the contract for the executable you are about to use.
Checkpoint
Record the version in your change review. If go help fix does not show -diff, stop and read that version's help before continuing.
2. Start from a recoverable working tree
Inspect the repository before allowing a source rewrite:
$ git status --short
$ git diff --check
An empty status is the clearest starting point. If you already have work in progress, commit it or use a separate worktree. This is a safety boundary rather than a requirement go fix imposes itself: a mixed diff makes it hard to tell which lines the tool changed, and that makes recovery easy to get wrong.
Warning
Do not treat go fix as a formatting-only command. Its registered analyzers can modernise APIs, rewrite loop code, change build directives, and apply source-level fix directives. Review every resulting file.
3. List the fixers before selecting one
Ask the tool which fixers it has installed:
$ go tool fix help
Registered analyzers:
any replace interface{} with any
buildtag check //go:build and // +build directives
...
stringscutprefix replace HasPrefix/TrimPrefix with CutPrefix
...
The list is version-specific. On Go 1.26.1, the default suite includes fixers such as newexpr, rangeint, stringscutprefix, and waitgroup. Ask for one fixer's detail when you need to understand its scope:
$ go tool fix help stringscutprefix
Some older documentation calls these items fixes or rewrite rules. Current Go presents them as analysis-based fixers. The practical rule stays the same either way: read the installed description, because a fixer added in a later release will not exist in an older toolchain.
4. Preview the whole package selection
Run the preview from the module root. ./... means all packages below the current module path that the Go command can resolve:
$ go fix -diff ./...
--- /path/to/project/file.go (old)
+++ /path/to/project/file.go (new)
@@
- message = strings.TrimPrefix(message, "he")
+ message = after
With the installed Go 1.26.1 command, -diff prints a unified diff and leaves the files unchanged. An empty output means this run found no applicable changes; it does not prove the code is correct, or that every desired API migration is available, only what this tool run would change.
Checkpoint
Save or review the preview before applying it:
$ go fix -diff ./... > /tmp/project-go-fix.patch
$ sed -n '1,160p' /tmp/project-go-fix.patch
The temporary patch is optional and contains source text, so do not leave confidential code sitting in a shared temporary directory. Remove it after review with rm -- /tmp/project-go-fix.patch once you are sure it is no longer useful.
5. Narrow the scope when the diff is too broad
A package pattern is part of the operation, not a comment for the reader. To inspect one package, use its module-relative path:
$ go fix -diff ./internal/parser
To use one named fixer while keeping the command's other fix flags, pass its analyser flag after go fix:
$ go fix -diff -stringscutprefix ./internal/parser
Check the installed help for the exact flag name and whether it is enabled by default. A fixer flag is not an API compatibility switch: it does not make code targeting an older Go release safe to change without testing the module's declared Go version and supported toolchains.
For a repository with generated code, vendored code, or several modules, inspect the package list first with go list ./.... Exclude paths deliberately rather than relying on a shell glob that happens to omit them.
6. Apply only an understood diff
Once the preview is acceptable and the working tree is clean, apply the same package selection:
$ go fix ./...
$ git diff --stat
$ git diff --check
There is normally no success message. The changed files and the Git diff are the output. The safest recovery path is to inspect the diff, run the project's tests, and then either commit it as a focused change or discard the whole thing with your normal version-control workflow.
Recovery
If you created a commit immediately before the operation, git revert COMMIT_ID is a recoverable undo for that commit. Do not use a broad reset when the tree holds work you have not backed up.
Run the project's normal checks after applying fixes:
$ go test ./...
$ go vet ./...
These commands check different things. A successful go fix run only means the suggested edits were applied; it is not a replacement for compilation, tests, review, or compatibility testing on the oldest Go version you support.
Common traps
- Empty preview. Check the Go version, module root, package pattern, and fixer list. The current suite may simply have no applicable edits.
- Package fails to load. Resolve the module or build-constraint error before trying to interpret fixer output.
- Rejected flag. Run
go help fixandgo tool fix helpagain. The installed manpage may describe a different generation of the command, and an online example may target a newer release. - Generated files touched. Verify how the project regenerates them before committing either the generated result or the source change.
- Using it to force a build to pass.
go fixapplies registered safe transformations, not arbitrary dependency upgrades or semantic migrations. Changes needing design decisions belong in a separate, deliberate edit, tested on its own.
Done means
- Toolchain recorded: you noted the actual
gobinary and version. - Clean start: the working tree was clean, or existing work was safely separated first.
- Fixers reviewed: you listed the installed fixers and previewed the exact package selection.
- Diff understood: the applied diff contains only changes you actually understand.
- Checks pass:
go test ./...,go vet ./..., and the project's review checks all pass. - Backup exists: you have a commit or backup that can restore the pre-fix source.