This project implements a Rewrite module that provides automated code quality, migration, and remediation recipes for Go codebases. Built on the rewrite-go framework, these recipes analyze and transform Go source code at the AST level, enabling safe, large-scale automated refactoring.
Note: These recipes are currently only supported via the Moderne CLI or the Moderne Platform. The Moderne CLI is free to use for open-source repositories. If your repository is closed-source, you will need to obtain a license to use the CLI or the Moderne Platform. Please contact Moderne to learn more.
Go is not part of the default Moderne CLI build pipeline. You must opt in by adding a build.steps configuration. Create or edit ~/.moderne/cli/moderne.yml:
build.steps:
- type: goAlternatively, place a .moderne/moderne.yml file in your repository root with the same content.
Install the recipes using the Moderne CLI's mod config recipes go install command. Go recipe modules are resolved from source via their Go module path.
mod config recipes go install github.com/moderneinc/recipes-go@v0.4.0mod config recipes go install github.com/moderneinc/recipes-gomod config recipes go delete github.com/moderneinc/recipes-gomod config recipes listBuild an LST first, then run recipes against it:
# Build the LST for your Go repository
mod build . --no-download
# Run a specific recipe
mod run . --recipe org.openrewrite.golang.codequality.PreferErrorsIsOverEquality
# Run a simplification recipe
mod run . --recipe org.openrewrite.golang.codequality.SimplifyBooleanExpression213 recipes across 6 categories with 595 tests.
| Category | Recipes | Description |
|---|---|---|
| Style | 73 | Enforce conventions, detect code smells, security patterns, resource management |
| Simplification | 61 | Modernize code with newer stdlib APIs, simplify expressions, migrate deprecated APIs |
| Error Handling | 27 | errors.Is/errors.As migration, error wrapping, sentinel extraction |
| Redundancy | 23 | Remove dead code, redundant operations, unreachable statements |
| Performance | 20 | Loop optimizations, allocation hoisting, format string improvements |
| Naming | 9 | Receiver names, stuttering, constants, getter prefixes, error variables |
- ioutil to io/os migration:
ioutil.ReadAlltoio.ReadAll,ioutil.ReadFiletoos.ReadFile, etc. (8 recipes) - strings/bytes Index to Contains family:
strings.Index(s, sub) != -1tostrings.Contains(s, sub)(7 recipes) - Boolean simplification:
x == truetox,x == falseto!x,!!xtox - fmt.Sprintf optimization:
fmt.Sprintf("%s", s)tos,fmt.Sprintf("%v", x)tofmt.Sprint(x) - Go 1.21+:
sort.Sort(sort.IntSlice(s))tosort.Ints(s),math.Min(a, b)tomin(a, b) - Structured logging:
log.Println(x)toslog.Info(x)(Go 1.21+)
- errors.Is migration:
err == io.EOFtoerrors.Is(err, io.EOF)for 8+ common sentinels - errors.As migration:
if myErr, ok := err.(*T); oktovar myErr *T; if errors.As(err, &myErr) - Error wrapping:
return errtoreturn fmt.Errorf("funcName: %w", err) - Sentinel extraction: Inline
errors.New("msg")to package-levelvar ErrMsgdeclarations - fmt.Errorf verb:
fmt.Errorf("...: %s", err)tofmt.Errorf("...: %w", err) - Swallowed error:
if err != nil { return }toif err != nil { return err }
- Resource management: Auto-insert
defer f.Close(),defer rows.Close(),defer ticker.Stop(), etc. - TLS enforcement:
InsecureSkipVerify: truetoInsecureSkipVerify: false - File permissions:
0777to0755 - Credential remediation:
var password = "hunter2"tovar password = os.Getenv("PASSWORD") - Doc comments: Auto-generate
// FuncName ...stubs for exported functions - Raw regex strings:
regexp.Compile("\\d+")toregexp.Compile(`\d+`)
- Dead code removal: Unreachable code after return, empty loops/switches/defaults, self-assignments
- Control flow simplification:
if true {body}tobody,if len(s) > 0 { for range s }tofor range s - Goroutine simplification:
go func() { f() }()togo f() - Map clearing:
for k := range m { delete(m, k) }toclear(m)
- Regex hoisting:
regexp.MustCompile("pattern")inside loops hoisted before loop - Format string optimization:
fmt.Sprintf("%d", n)tostrconv.Itoa(n) - Loop detection: Allocations, defer, goroutine launches, lock acquisition in loops
- Receiver names:
func (self *Foo) Bar()tofunc (f *Foo) Bar() - Getter prefix:
func (u *User) GetName()tofunc (u *User) Name() - Stuttering:
func HttpGet()inpackage httptofunc Get() - Constants:
MAX_BUFFER_SIZEtoMaxBufferSize - Error variables:
var notFound = errors.New(...)tovar ErrNotFound = errors.New(...)
For help getting started with the Moderne CLI, check out our getting started guide. Or, if you'd like to try running these recipes in the Moderne Platform, check out the Moderne Platform quickstart guide.
go test ./... -count=1 # Run all tests
go test ./tests/simplification/ # Run a specific category
./gradlew check # Full build via GradleReleases are cut by pushing a semver tag from main:
git tag v0.1.0
git push origin v0.1.0The publish workflow (.github/workflows/publish.yml) reacts to tags matching
vX.Y.Z or vX.Y.Z-rc.N and delegates to the shared
openrewrite/gh-automation publish-gradle.yml workflow, which publishes the
recipe-library Maven artifact (catalog metadata) to Maven Central via OSSRH.
The Go module itself is consumed by the Moderne CLI directly from the Go module
proxy (proxy.golang.org). The module lives at the repository root, so a single
plain vX.Y.Z tag serves both the Maven publish and the Go module — no
subdirectory-prefixed tag is needed. No active push is needed for the Go side —
once the tag exists on GitHub, mod config recipes go install github.com/moderneinc/recipes-go@vX.Y.Z resolves it.
This project is licensed under the Moderne Proprietary License. Only for use by Moderne customers under the terms of a commercial contract.