Using go fix
Go continues to evolve with cleaner syntax, better standard library functions and improved language semantics. Keeping a large codebase up to date manually can be time-consuming and error-prone. Fortunately, the go fix command (significantly enhanced in Go 1.26) makes modernization effortless and safe.
go fix now uses the same powerful analysis framework as go vet. It applies a suite of modernizers — automated, semantics-preserving transformations that update outdated patterns to use the latest Go idioms and library features.
Run it with a single command:
$ go fix ./...
This scans your entire module and rewrites files where improvements are possible. Use go fix -diff ./... to preview changes without modifying anything.
Here is a complete, self-contained Go source file that demonstrates many common patterns go fix can modernize automatically. Save this as oldstyle.go and run go fix ./... (or go fix -diff ./...) to see the transformations in action.
package main
import (
"fmt"
"strings"
)
type RequestJSON struct {
URL string
Attempts int
}
func main() {
// 1. interface{} → any (modernizer: any)
var val any = 42
fmt.Println("any value:", val)
// 2. Redundant loop variable capture (pre-Go 1.22 pattern)
// modernizer: forvar
s := []int{10, 20, 30}
for _, x := range s {
x := x // redundant after Go 1.22
fmt.Println("loop var:", x)
}
// 3. Manual string splitting instead of strings.Cut
// modernizer: stringscut
pair := "key=value"
eq := strings.IndexByte(pair, '=')
if eq >= 0 {
fmt.Println("key:", pair[:eq])
fmt.Println("value:", pair[eq+1:])
}
// 4. Manual min/max clamping
// modernizer: minmax
x := someComputation()
if x < 0 {
x = 0
}
if x > 100 {
x = 100
}
fmt.Println("clamped value:", x)
// 5. Classic integer for loop
// modernizer: rangeint
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
fmt.Println()
// 6. new(T) followed by assignment
// modernizer: newexpr (Go 1.26+)
ptr := new(int)
*ptr = 42
fmt.Println("new expr value:", *ptr)
// 7. Using new() directly with value (Go 1.26+)
// This will be used in the struct literal below
attempts := new(5) // modern syntax already, but shown for contrast
fmt.Println("attempts via new():", *attempts)
// Example using new(10) directly in struct literal (no helper needed)
data, err := jsonMarshal(&RequestJSON{
URL: "https://example.com",
Attempts: 10, // direct int value
})
if err != nil {
fmt.Println("error:", err)
}
fmt.Println("JSON data:", string(data))
}
func someComputation() int {
return -5 + randInt()
}
func randInt() int {
return 42 // placeholder
}
// Placeholder for json.Marshal to keep the example self-contained
func jsonMarshal(v any) ([]byte, error) {
return []byte(`{"url":"https://example.com","attempts":10}`), nil
}
After running go fix ./..., the code becomes significantly cleaner:
interface{}turns intoany- Redundant
x := xdeclarations disappear - Manual
IndexByte+ slicing becomesstrings.Cut - Clamping logic collapses into
min(max(...)) - The integer loop may simplify with
range new(int); *ptr = nbecomes the newnew(n)syntax (Go 1.26+)- Calls to helper functions like
newIntcan be inlined tonew(...)
Let us see the output of go fix -diff ./...:
$ pwd
/Users/mtsouk/go/src/github.com/mactsouk/goVet
$ go mod init
go: creating new go.mod: module github.com/mactsouk/goVet
go: to add module requirements and sums:
go mod tidy
$ go mod tidy
$ go fix -diff ./...
--- /Users/mtsouk/go/src/github.com/mactsouk/goVet/oldStyle.go (old)
+++ /Users/mtsouk/go/src/github.com/mactsouk/goVet/oldStyle.go (new)
@@ -19,25 +19,21 @@
// modernizer: forvar
s := []int{10, 20, 30}
for _, x := range s {
- x := x // redundant after Go 1.22
- fmt.Println("loop var:", x)
+ fmt.Println("loop var:", x)
}
// 3. Manual string splitting instead of strings.Cut
// modernizer: stringscut
pair := "key=value"
- eq := strings.IndexByte(pair, '=')
- if eq >= 0 {
- fmt.Println("key:", pair[:eq])
- fmt.Println("value:", pair[eq+1:])
+ before, after, ok := strings.Cut(pair, "=")
+ if ok {
+ fmt.Println("key:", before)
+ fmt.Println("value:", after)
}
// 4. Manual min/max clamping
// modernizer: minmax
- x := someComputation()
- if x < 0 {
- x = 0
- }
+ x := max(someComputation(), 0)
if x > 100 {
x = 100
}
@@ -45,7 +32,7 @@
// 5. Classic integer for loop
// modernizer: rangeint
- for i := 0; i < 10; i++ {
+ for i := range 10 {
fmt.Print(i, " ")
}
fmt.Println()
$ go fix ./...
$ go fix -diff ./...
--- /Users/mtsouk/go/src/github.com/mactsouk/goVet/oldStyle.go (old)
+++ /Users/mtsouk/go/src/github.com/mactsouk/goVet/oldStyle.go (new)
@@ -33,10 +33,7 @@
// 4. Manual min/max clamping
// modernizer: minmax
- x := max(someComputation(), 0)
- if x > 100 {
- x = 100
- }
+ x := min(max(someComputation(), 0), 100)
fmt.Println("clamped value:", x)
// 5. Classic integer for loop
$ go fix ./...
After reviewing the diff, we ran go fix ./... to apply the changes. Then we ran it again — sometimes a second pass catches more improvements. In this case, the minmax improvement (min(max(...), 100)).
You can purchase Practical Systems Programming in Go from amazon.com or Packt Publishing. All code from the book is available in the official GitHub repository.
You can get Mastering Go, 4th edition on Packt, Amazon.com, all other Amazon web sites as well as on other book stores.