Upgrading Go Code with Enhanced go fix Tools
Revamping Go Code with go fix: What to Know
Alan Donovan | February 17, 2026
The recent 1.26 release of Go brings with it a significant enhancement: the go fix subcommand has been completely overhauled. This isn't just a minor update; the new go fix employs advanced algorithms to pinpoint areas where your Go code can be refined, optimizing it for the latest language features and library capabilities. In this article, we’ll explore how you can effectively utilize go fix to upgrade your codebase. Then, we’ll take a closer look at the underlying infrastructure that powers this tool and its ongoing evolution. Lastly, we’ll discuss "self-service" analysis tools that empower developers and organizations to establish and follow best practices with greater ease.
Executing go fix: A Straightforward Approach
The go fix command operates similarly to go build and go vet, accepting package patterns to determine which parts of your codebase to improve. For instance, executing the command below will apply fixes to all packages located in the current directory and its subdirectories:
$ go fix ./...
On successful execution, go fix updates your source files quietly, skipping over any generated files since the fix in such scenarios should address the logic within the generator, rather than the generated output itself. It’s a good practice to run go fix whenever you upgrade your Go toolchain; doing so minimizes the risk of introducing complex changes into your version control history. By keeping a clean Git slate, you’ll make the code review process easier for your collaborators.
If you're curious about what changes would have been made by this command, you can use the -diff flag to preview potential modifications:
$ go fix -diff ./...
--- dir/file.go (old)
+++ dir/file.go (new)
- eq := strings.IndexByte(pair, '=')
- result[pair[:eq]] = pair[1+eq:]
+ before, after, _ := strings.Cut(pair, "=")
+ result[before] = after
…
Additionally, you can view a list of available fixers by running:
$ go tool fix help
…
Registered analyzers:
any replace interface{} with any
buildtag check //go:build and // +build directives
fmtappendf replace []byte(fmt.Sprintf) with fmt.Appendf
forvar remove redundant re-declaration of loop variables
hostport check format of addresses passed to net.Dial
inline apply fixes based on 'go:fix inline' comment directives
mapsloop replace explicit loops over maps with calls to maps package
minmax replace if/else statements with calls to min or max
…
If you're interested in a specific analyzer, adding its name will reveal detailed documentation. For instance:
$ go tool fix help forvar
forvar: remove redundant re-declaration of loop variables
The forvar analyzer removes unnecessary shadowing of loop variables.
Before Go 1.22, it was common to write `for _, x := range s { x := x ... }`
to create a fresh variable for each iteration. Go 1.22 changed the semantics
of `for` loops, making this pattern redundant. This analyzer removes the
unnecessary `x := x` statement.
This fix only applies to `range` loops.
By default, go fix runs all analyzers, but if you’re working on a large codebase, you might want to streamline the review process by applying fixes from the most common analyzers first. To achieve this, use flags corresponding to the desired analyzers. For example, to run just the any fixer, use the -any flag. Alternatively, to include everything except certain analyzers, negate the flags, like -any=false.
As with its counterparts, go fix focuses on a specific build configuration each time it's executed. For large projects that involve files tailored for various CPUs or platforms, running the command multiple times might be beneficial for comprehensive coverage:
$ GOOS=linux GOARCH=amd64 go fix ./...
$ GOOS=darwin GOARCH=arm64 go fix ./...
$ GOOS=windows GOARCH=amd64 go fix ./...
There’s an added potential for synergistic improvements when running the command multiple times, which we’ll explore shortly.
Modernizing Code: The Role of Modernizers
The introduction of generics in Go 1.18 has marked a notable shift in the Go programming model. Developers can now express previously cumbersome loops—involving situations like gathering keys from a map—through simpler constructs like maps.Keys. This shift opens the door to streamlining and modernizing existing code, making it clearer and more efficient.
With the widespread adoption of machine learning coding assistants, we found they often generated Go code aligned with older styles, failing to leverage new idioms introduced in more recent versions. Even when prompted to use updated conventions, these tools sometimes ignored the latest features. A significant focus, therefore, should be on ensuring that the current best practices in Go are part of the training data, ideally filtering their usage into the global repository of open-source Go code.
In the past year, developers have crafted numerous analyzers designed to highlight modernization opportunities. For example, here’s what some of these fixes look like:
minmax suggests replacing an if statement with the cleaner calls to Go’s min or max functions, as showcased below:
x := f()
if x < 0 {
x = 0
}
if x > 100 {
x = 100
}
x := min(max(f(), 0), 100)
rangeint simplifies a traditional three-clauses loop to a Go 1.22 range-over-int loop:
for i := 0; i < n; i++ {
f()
}
for range n {
f()
}
stringscut (previously shown in the -diff output) optimizes code by switching out strings.Index and slicing with Go 1.18’s strings.Cut:
i := strings.Index(s, ":")
if i >= 0 {
return s[:i]
}
before, _, ok := strings.Cut(s, ":")
if ok {
return before
}
Modernizers, integrated into gopls, provide real-time feedback while coding and enhance the go fix experience, allowing for large-scale package modernization in just one command. These tools are not just time-savers; they are also educational, guiding Go developers towards contemporary practices. Importantly, every proposal for changes to the language and libraries now considers whether a corresponding modernizer should be introduced, ensuring that future iterations of Go maintain a focus on best practices.
As we begin to explore more features of the 1.26 update, let's turn our attention to an exciting new addition: the updated behavior of the built-in new function.