NewsWorld
PredictionsDigestsScorecardTimelinesArticles
NewsWorld
HomePredictionsDigestsScorecardTimelinesArticlesWorldTechnologyPoliticsBusiness
AI-powered predictive news aggregation© 2026 NewsWorld. All rights reserved.
Trending
TrumpTariffTradeAnnounceLaunchNewsPricesStrikesMajorFebruaryPhotosYourCarLotSayCourtDigestSundayTimelineSafetyGlobalMarketTechChina
TrumpTariffTradeAnnounceLaunchNewsPricesStrikesMajorFebruaryPhotosYourCarLotSayCourtDigestSundayTimelineSafetyGlobalMarketTechChina
All Articles
Using go fix to modernize Go code
Hacker News
Published 5 days ago

Using go fix to modernize Go code

Hacker News · Feb 17, 2026 · Collected from RSS

Summary

Article URL: https://go.dev/blog/gofix Comments URL: https://news.ycombinator.com/item?id=47049479 Points: 22 # Comments: 2

Full Article

The 1.26 release of Go this month includes a completely rewritten go fix subcommand. Go fix uses a suite of algorithms to identify opportunities to improve your code, often by taking advantage of more modern features of the language and library. In this post, we’ll first show you how to use go fix to modernize your Go codebase. Then in the second section we’ll dive into the infrastructure behind it and how it is evolving. Finally, we’ll present the theme of “self-service” analysis tools to help module maintainers and organizations encode their own guidelines and best practices. Running go fix The go fix command, like go build and go vet, accepts a set of patterns that denote packages. This command fixes all packages beneath the current directory: $ go fix ./... On success, it silently updates your source files. It discards any fix that touches generated files since the appropriate fix in that case is to the logic of the generator itself. We recommend running go fix over your project each time you update your build to a newer Go toolchain release. Since the command may fix hundreds of files, start from a clean git state so that the change consists only of edits from go fix; your code reviewers will thank you. To preview the changes the above command would have made, use the -diff flag: $ 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 … You can list the available fixers by running this command: $ 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 … Adding the name of a particular analyzer shows its complete documentation: $ 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, the go fix command runs all analyzers. When fixing a large project it may reduce the burden of code review if you apply fixes from the most prolific analyzers as separate code changes. To enable only specific analyzers, use the flags matching their names. For example, to run just the any fixer, specify the -any flag. Conversely, to run all the analyzers except selected ones, negate the flags, for instance -any=false. As with go build and go vet, each run of the go fix command analyzes only a specific build configuration. If your project makes heavy use of files tagged for different CPUs or platforms, you may wish to run the command more than once with different values of GOARCH and GOOS for better coverage: $ GOOS=linux GOARCH=amd64 go fix ./... $ GOOS=darwin GOARCH=arm64 go fix ./... $ GOOS=windows GOARCH=amd64 go fix ./... Running the command more than once also provides opportunities for synergistic fixes, as we’ll see below. Modernizers The introduction of generics in Go 1.18 marked the end of an era of very few changes to the language spec and the start of a period of more rapid—though still careful—change, especially in the libraries. Many of the trivial loops that Go programmers routinely write, such as to gather the keys of a map into a slice, can now be conveniently expressed as a call to a generic function such as maps.Keys. Consequently these new features create many opportunities to simplify existing code. In December 2024, during the frenzied adoption of LLM coding assistants, we became aware that such tools tended—unsurprisingly—to produce Go code in a style similar to the mass of Go code used during training, even when there were newer, better ways to express the same idea. Less obviously, the same tools often refused to use the newer ways even when directed to do so in general terms such as “always use the latest idioms of Go 1.25.” In some cases, even when explicitly told to use a feature, the model would deny that it existed. (See my 2025 GopherCon talk for more exasperating details.) To ensure that future models are trained on the latest idioms, we need to ensure that these idioms are reflected in the training data, which is to say the global corpus of open-source Go code. Over the past year, we have built dozens of analyzers to identify opportunities for modernization. Here are three examples of the fixes they suggest: minmax replaces an if statement by a use of Go 1.21’s min or max functions: x := f() if x < 0 { x = 0 } if x > 100 { x = 100 } x := min(max(f(), 0), 100) rangeint replaces a 3-clause for loop by a Go 1.22 range-over-int loop: for i := 0; i < n; i++ { f() } for range n { f() } stringscut (whose -diff output we saw earlier) replaces uses of strings.Index and slicing by 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 } These modernizers are included in gopls, to provide instant feedback as you type, and in go fix, so that you can modernize several entire packages at once in a single command. In addition to making code clearer, modernizers may help Go programmers learn about newer features. As part of the process of approving each new change to the language and standard library, the proposal review group now considers whether it should be accompanied by a modernizer. We expect to add more modernizers with each release. Example: a modernizer for Go 1.26’s new(expr) Go 1.26 includes a small but widely useful change to the language specification. The built-in new function creates a new variable and returns its address. Historically, its sole argument was required to be a type, such as new(string), and the new variable was initialized to its “zero” value, such as "". In Go 1.26, the new function may be called with any value, causing it to create a variable initialized to that value, avoiding the need for an additional statement. For example: ptr := new(string) *ptr = "go1.25" ptr := new("go1.26") This feature filled a gap that had been discussed for over a decade and resolved one of the most popular proposals for a change to the language. It is especially convenient in code that uses a pointer type *T to indicate an optional value of type T, as is common when working with serialization packages such as json.Marshal or protocol buffers. This is such a common pattern that people often capture it in a helper, such as the newInt function below, saving the caller from the need to break out of an expression context to introduce additional statements: type RequestJSON struct { URL string Attempts *int // (optional) } data, err := json.Marshal(&RequestJSON{ URL: url, Attempts: newInt(10), }) func newInt(x int) *int { return &x } Helpers such as newInt are so frequently needed with protocol buffers that the proto API itself provides them as proto.Int64, proto.String, and so on. But Go 1.26 makes all these helpers unnecessary: data, err := json.Marshal(&RequestJSON{ URL: url, Attempts: new(10), }) To help you take advantage of this feature, the go fix command now includes a fixer, newexpr, that recognizes “new-like” functions such as newInt and suggests fixes to replace the function body with return new(x) and to replace every call, whether in the same package or an importing package, with a direct use of new(expr). To avoid introducing premature uses of new features, modernizers offer fixes only in files that require at least the minimum appropriate version of Go (1.26 in this instance), either through a go 1.26 directive in the enclosing go.mod file or a //go:build go1.26 build constraint in the file itself. Run this command to update all calls of this form in your source tree: $ go fix -newexpr ./... At this point, with luck, all of your newInt-like helper functions will have become unused and may be safely deleted (assuming they aren’t part of a stable published API). A few calls may remain where it would be unsafe to suggest a fix, such as when the name new is locally shadowed by another declaration. You can also use the deadcode command to help identify unused functions. Synergistic fixes Applying one modernization may create opportunities to apply another. For example, this snippet of code, which clamps x to the range 0–100, causes the minmax modernizer to suggest a fix to use max. Once that fix is applied it suggests a second fix, this time to use min. x := f() if x < 0 { x = 0 } if x > 100 { x = 100 } x := min(max(f(), 0), 100) Synergies may also occur between different analyzers. For example, a common mistake is to repeatedly concatenate strings within a loop, resulting in quadratic time complexity—a bug and a potential vector for a denial-of-service attack. The stringsbuilder modernizer recognizes the problem and suggests using Go 1.10’s strings.Builder: s := "" for _, b := range bytes { s += fmt.Sprintf("%02x", b) } use(s) var s strings.Builder for _, b := range bytes { s.WriteString(fmt.Sprintf("%02x", b)) } use(s.String()) Once this fix is applied, a second analyzer may recognize that the WriteString and Sprintf operations can be combined as fmt.Fprintf(&s, "%02x", b), which is both cleaner and more efficient, and offer a second fix. (This second analyzer is QF1012 from Dominik Honnef’s staticcheck, which is already enabled in gopls but not yet in go fix, though we plan to add staticcheck analyzers to the go command starting in Go 1.27.) Consequ


Share this story

Read Original at Hacker News

Related Articles

Hacker Newsabout 2 hours ago
Volatility: The volatile memory forensic extraction framework

Article URL: https://github.com/volatilityfoundation/volatility3 Comments URL: https://news.ycombinator.com/item?id=47110781 Points: 3 # Comments: 0

Hacker Newsabout 2 hours ago
Holo v0.9: A Modern Routing Stack Built in Rust

Article URL: https://github.com/holo-routing/holo/releases/tag/v0.9.0 Comments URL: https://news.ycombinator.com/item?id=47110634 Points: 4 # Comments: 1

Hacker Newsabout 3 hours ago
The Dance Floor Is Disappearing in a Sea of Phones

Article URL: https://www.bloomberg.com/news/features/2026-02-20/a-boom-in-electronic-dance-music-is-changing-club-culture Comments URL: https://news.ycombinator.com/item?id=47110549 Points: 12 # Comments: 0

Hacker Newsabout 3 hours ago
Attention Media ≠ Social Networks

Article URL: https://susam.net/attention-media-vs-social-networks.html Comments URL: https://news.ycombinator.com/item?id=47110515 Points: 63 # Comments: 13

Hacker Newsabout 3 hours ago
Minions: Stripe's one-shot, end-to-end coding agents – Stripe Dot Dev Blog

Article URL: https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents Comments URL: https://news.ycombinator.com/item?id=47110495 Points: 36 # Comments: 29

Hacker Newsabout 3 hours ago
What Is a Database Transaction?

Article URL: https://planetscale.com/blog/database-transactions Comments URL: https://news.ycombinator.com/item?id=47110473 Points: 30 # Comments: 2