Skip to content
Development
Skill

/modern-go

Modernize Go code by applying version-appropriate idioms and APIs (gofix-style transformations). Scans go.mod for the Go version, then transforms Go source files to use modern patterns—from Go 1.0 through 1.26+. Use when the user says "现代化","现代Go语言", "地道的", "idiomatic",

From plugin
goal-workflow-skills
30120 skills

Claude Code

$ npx -y skills add smallnest/goal-workflow --skill modern-go --agent claude-code

SKILL.md

modern-go.SKILL.md
name: modern-go
description: Modernize Go code by applying version-appropriate idioms and APIs (gofix-style transformations). Scans go.mod for the Go version, then transforms Go source files to use modern patterns—from Go 1.0 through 1.26+. Use when the user says "现代化","现代Go语言", "地道的", "idiomatic",  "modernize", "modern-go", "update Go code", "gofix", or wants to upgrade Go idioms.

modern-go

Modernize Go source code by applying version-appropriate idioms, APIs, and language features. Works like `go fix` plus additional transformations curated from the Go team's modernize analysis passes and community best practices.

Usage

Invoke this skill when the user asks to modernize Go code. By default, modernize the entire project; the user may specify a file or directory instead.

When invoked: 1. Detect the project's Go version from `go.mod` (the `go` directive). 2. Find all `.go` files in the target scope (excluding `vendor/`, `.git/`, `testdata/`). 3. For each file, apply **all transformations for versions ≤ the project's Go version**, starting from the oldest to the newest. 4. After all transformations, print a summary of what was changed and what was skipped.

If the user specifies a file or directory, limit the scope to that path.

Transformation Catalog

Each transformation includes a **Go version** gate—only apply when the project's `go.mod` version ≥ that version. Never apply a transformation that requires a version higher than the project declares.

Go 1.0+ — `time.Since`

| Before | After | |---|---| | `time.Now().Sub(start)` | `time.Since(start)` |

// before
elapsed := time.Now().Sub(start)
// after
elapsed := time.Since(start)

Go 1.8+ — `time.Until`

| Before | After | |---|---| | `deadline.Sub(time.Now())` | `time.Until(deadline)` |

// before
remaining := deadline.Sub(time.Now())
// after
remaining := time.Until(deadline)

Go 1.10+ — `strings.Builder` (loop concatenation)

| Before | After | |---|---| | `s += item` in a loop | `var b strings.Builder; b.WriteString(item)` |

// before
s := ""
for _, item := range items {
    s += item
}
// after
var b strings.Builder
for _, item := range items {
    b.WriteString(item)
}
s := b.String()

Only when `+=` concatenation happens inside a loop.

Go 1.13+ — `errors.Is`

| Before | After | |---|---| | `err == io.EOF` | `errors.Is(err, io.EOF)` |

// before
if err == io.EOF {
    return
}
// after
if errors.Is(err, io.EOF) {
    return
}

Go 1.17+ — `//go:build` constraints (plusbuild)

| Before | After | |---|---| | `// +build linux` + `//go:build linux` (both present) | keep only `//go:build linux` |

// before
//go:build linux && amd64
// +build linux,amd64

package foo
// after
//go:build linux && amd64

package foo

The `plusbuild` modernizer removes obsolete `// +build` constraint lines once the equivalent `//go:build` line is present (the `//go:build` syntax landed in Go 1.17). Only strip the old line when a matching `//go:build` already exists — never drop the sole constraint.

Go 1.17+ — `unsafe.Add` / `unsafe.Slice` (unsafefuncs)

| Before | After | |---|---| | `unsafe.Pointer(uintptr(ptr) + uintptr(n))` | `unsafe.Add(ptr, n)` | | `(*[n]T)(unsafe.Pointer(p))[:]` slice construction | `unsafe.Slice(p, n)` |

// before — pointer arithmetic via uintptr
p2 := unsafe.Pointer(uintptr(ptr) + uintptr(offset))
// after
p2 := unsafe.Add(ptr, offset)
// before — building a slice from a base pointer
s := (*[1 << 30]byte)(unsafe.Pointer(p))[:n:n]
// after
s := unsafe.Slice(p, n)

The `unsafefuncs` modernizer (gopls v0.22.0) rewrites error-prone `uintptr` pointer math into `unsafe.Add` / `unsafe.Slice`, which the compiler and `go vet` understand as GC-safe.

Go 1.18+ — `any`

| Before | After | |---|---| | `interface{}` | `any` |

// before
func decode(v interface{}) error { ... }
// after
func decode(v any) error { ... }

Go 1.18+ — `strings.Cut`

| Before | After | |---|---| | `i := strings.Index(s, sep); ... s[:i], s[i+len(sep):]` | `key, val, found := strings.Cut(s, sep)` |

// before
if i := strings.Index(s, "="); i >= 0 {
    key, val := s[:i], s[i+1:]
}
// after
if key, val, found := strings.Cut(s, "="); found {
    ...
}

Go 1.18+ — `bytes.Cut`

| Before | After | |---|---| | `i := bytes.Index(b, sep); ... b[:i], b[i+len(sep):]` | `before, after, found := bytes.Cut(b, sep)` |

// before
if i := bytes.Index(b, sep); i >= 0 {
    before, after := b[:i], b[i+len(sep):]
}
// after
before, after, found := bytes.Cut(b, sep)

Go 1.19+ — `fmt.Appendf`

| Before | After | |---|---| | `buf = append(buf, fmt.Sprintf(...)...)` | `buf = fmt.Appendf(buf, ...)` |

// before
buf = append(buf, fmt.Sprintf("x=%d", x)...)
// after
buf = fmt.Appendf(buf, "x=%d", x)

Go 1.19+ — Type-safe atomics (atomictypes)

| Before | After | |---|---| | `atomic.StoreInt32(&v, 1)` / `atomic.LoadInt32(&v)` | `var v atomic.Int32; v.Store(1); v.Load()` | | `atomic.AddInt64(&v, 1)` | `var v atomic.Int64; v.Add(1)` | | `atomic.Value` + type assertion | `atomic.Pointer[T]` |

// before
var ready int32
atomic.StoreInt32(&ready, 1)
if atomic.LoadInt32(&ready) == 1 { ... }

// after
var ready atomic.Int32
ready.Store(1)
if ready.Load() == 1 { ... }
// before
var cache atomic.Value
cache.Store(&Config{})
cfg := cache.Load().(*Config)

// after
var cache atomic.Pointer[Config]
cache.Store(&Config{})
cfg := cache.Load()

The `atomictypes` modernizer (gopls v0.22.0, `AtomicTypesAnalyzer`) rewrites both the variable declaration and every call site. Typed wrappers (`atomic.Int32/Int64/Uint32/Uint64/Bool/Pointer[T]`) have identical performance but prevent accidental non-atomic access and fix 64-bit alignment crashes on 32-bit architectures.

Go 1.20+ — `strings.Clone`

| Before | After | |---|---| | `string([]byte(s))` | `strings.Clone(s)` |

// before
s2 := string([]byte(s)) // force
Read more
@smallnest
Ships withgoal-workflow-skills

An AI-driven development workflow — from PRD to shipped code, all within Claude Code.

Stars on GitHub

Momentum

stars in total
301
Star history of smallnest/goal-workflow on GitHub, a chart by star-history.com

Other skills on goal-workflow-skills.

design-it

design-it

Use when turning a requirement, spec, or feature brief into a single self-contained HTML design document in a fixed house style —…

graph

graph

Graph engineering for parallel task execution: convert a task, PRD, SPEC, or issue set into a dependency graph (DAG), layer it…

humanize-it

humanize-it

对指定文档进行去 AI 味的改写。自动选择最合适的人性化策略(humanizer-zh / humanize-chinese / technical-writing), 迭代改写直到效果达标或迭代 42 次为止。适用于中文文本的去 AI…

insight-diagram

insight-diagram

为任意项目生成 UML 图、架构图和流程图。分析代码库后让用户选择要生成的图表类型,使用 architecture-diagram skill 渲染为 HTML+SVG,保存到 docs/ 目录。适用于任何软件项目的文档可视化。

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about modern-go.

Which coding agents can use modern-go?
modern-go installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.