Modernizing Go Codebases with go fix: A Complete Guide
Getting Started with go fix
The go fix command, bundled with the Go toolchain, has received a complete overhaul in the Go 1.26 release. Its purpose is to automatically update your code to use modern idioms, improved APIs, and safer patterns. Running it over your project can save hours of manual refactoring. To fix all packages under the current directory, simply use:
$ go fix ./...On success, the command silently modifies your source files. It skips generated files (like those ending in _test.go if they are outputs of generators), since the correct fix for those lies in the generator logic itself. A best practice is to run go fix each time you upgrade your Go toolchain version. Before doing so, ensure your working tree is clean (git status) so that you can easily review the changes. This makes code review straightforward: all modifications come from the fixer.
If you want to preview changes without applying them, pass the -diff flag:
$ go fix -diff ./...This shows the diff of each file that would be altered. For example, a common fix replaces an explicit strings.IndexByte + slicing with the cleaner strings.Cut:
- eq := strings.IndexByte(pair, '=')
- result[pair[:eq]] = pair[1+eq:]
+ before, after, _ := strings.Cut(pair, "=")
+ result[before] = afterExploring the Available Fixers
To list all registered fixers (called analyzers), run:
$ go tool fix helpYou’ll see entries like:
- any – replace
interface{}withany - buildtag – check
//go:buildand// +builddirectives - fmtappendf – replace
[]byte(fmt.Sprintf)withfmt.Appendf - forvar – remove redundant re‑declaration of loop variables (see details below)
- hostport – check address format passed to
net.Dial - inline – apply fixes based on
//go:fix inlinecomment directives - mapsloop – replace explicit loops over maps with calls to
mapspackage - minmax – replace
if/elsewith calls tominormax
For detailed documentation on a specific analyzer, append its name:
$ go tool fix help forvarThis prints a description, an example, and any configuration options. The forvar analyzer, for instance, eliminates unnecessary shadowing of loop variables – a pattern that was common before Go 1.22, when the loop variable was reused across iterations. The fixer rewrites the code to avoid the shim variable entirely.
Example: Removing Loop Variable Shadowing
Suppose you have:
for _, v := range list {
v := v // duplicate
go func() {
fmt.Println(v)
}()
}The forvar fixer will remove the inner v := v line because Go 1.22 changed loop variable semantics to be per-iteration. After fixing, the code becomes:
for _, v := range list {
go func() {
fmt.Println(v)
}()
}This is both cleaner and safer.

The Infrastructure Behind go fix
The rewritten go fix is built on a framework that makes it easy to add new analyzers. Each fixer is a Go program that conforms to a specific interface. The command orchestrates them, applying fixes in a consistent order and handling file I/O. The infrastructure supports:
- Running analyzers across all packages in parallel
- Detecting generated files (using the standard
// Code generated …comment) to skip them - Producing diffs when the
-diffflag is used - Validating that the resulting code compiles (by default, after fixing,
go buildis run on the modified packages)
This evolution makes go fix a powerful tool not only for the Go team but also for the community, as any Go developer can write and contribute a fixer.
Self‑Service Analysis for Organizations
A major theme in the Go 1.26 release is enabling self-service analysis. Module maintainers and organizations can encode their own guidelines as custom fixers. For example, a company might require that all HTTP handlers use a specific logging wrapper. They can write a fixer that scans for raw http.HandlerFunc uses and suggests (or automatically applies) the wrapper. Similarly, organizations can enforce internal API deprecations or style rules without waiting for an upstream change.
To create a custom fixer, you implement the analysis.Analyzer interface from the golang.org/x/tools/go/analysis package. The fixer’s Run function receives a pass object that gives access to the package’s AST, types, and report function. You can then traverse the AST, identify patterns, and call pass.Reportf with a suggested fix (a set of text edits). Once compiled, you can invoke your fixer with:
$ go tool fix -fix myfixer ./...This extensibility means that go fix is no longer limited to what the Go team provides – it becomes a platform for code modernization across the entire ecosystem.
Ready to streamline your Go codebase? Start by running go fix ./... on your project today, and explore the available fixers to see what improvements are possible.
Related Articles
- How Programming Has (and Hasn't) Changed: The Enduring Challenges and the Game-Changing Impact of Stack Overflow
- Automating Intellectual Toil: Q&A on Agent-Driven Development with Copilot
- Pyroscope 2.0: Smarter, Cheaper Continuous Profiling for Modern Observability
- Tech Lead Reveals Simple Documentation Fix for AI-Generated Code That Passes Tests but Breaks Architecture
- 10 Key Insights into the Lomiri Tech Meeting: A Free Open Source Mobile Dev Hackathon in the Netherlands
- Critical Modbus Float32 Bug Plagues Industrial Controllers: 1.4e-45 Temperature Readings Signal Byte-Order Chaos
- Go 1.26 Unleashes Completely Rewritten 'Go Fix' Tool to Modernize Codebases
- How to Build a .NET AI Orchestration Library: A Step-by-Step Guide