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
- Go 1.26 Released: New Language Features, Performance Gains, and Experimental SIMD Support
- The Key to Effortless Unit Testing: Prime Testable Code
- Maximizing Your Impact: Participating in the 2025 Go Developer Survey
- Java Ecosystem Braces for Emergency Security Fixes, AI Debugging Breakthroughs, and Major JEP Milestones
- Structured-Prompt-Driven Development: A Team Approach to AI-Assisted Coding
- How to Reduce Heap Allocations by Stack-Allocating Slices in Go
- Exploring Python 3.15.0 Alpha 2: What Developers Need to Know
- CommitAI: Your Offline Git Assistant Powered by Gemma 4