Preview and Apply Go API Modernisation with go fix
You will use go fix to find source changes supplied by the installed Go toolchain, review them as a patch, and apply them only after your package still builds and the diff is understood. 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. 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, not a requirement imposed by go fix: a mixed diff makes it difficult to tell which lines the tool changed and 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 is the same: 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 that the code is correct or that every desired API migration is available; it only describes 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 in a shared temporary directory. Remove it after review with rm -- /tmp/project-go-fix.patch only when 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 retaining 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 that targets 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 is to inspect the diff, run the project's tests, and then either commit it as a focused change or discard the complete change with your normal version-control workflow. 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 contains 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 means that suggested edits were applied; it is not a replacement for compilation, tests, review, or compatibility testing on the oldest Go version you support.
7. Diagnose the common traps
If the preview is empty, check the Go version, module root, package pattern, and fixer list. The current suite may simply have no applicable edits. If a package cannot be loaded, resolve that module or build-constraint error before interpreting fixer output.
If a flag is rejected, run go help fix and go tool fix help again. The installed manpage may describe a different generation of the command, and an online example may target a newer release. If the diff touches generated files, verify how the project regenerates them before committing either the generated result or the source change.
Do not use go fix to repair a failing build by trial and error. It applies registered safe transformations, not arbitrary dependency upgrades or semantic migrations. For changes that require design decisions, update the code deliberately, test it, and keep that work separate from an automated fixer pass.
Done means
- You recorded the actual
gobinary and version. - The starting tree was clean or its existing work was safely separated.
- You listed the installed fixers and previewed the exact package selection.
- The applied diff contains only changes you understand.
go test ./...,go vet ./..., and the project's review checks pass.- You have a commit or backup that can restore the pre-fix source.