关于 go-modernize
面向 Go 项目的渐进式现代化技能,指导代码采用模块 Go 指令所支持的特性,并涵盖升级流程及语言和标准库迁移。
主要功能
- 01渐进式现代化工作模式
- 02按 Go 版本整理的特性参考
- 03官方现代化工具使用指引
- 04标准库 API 迁移示例
- 05检查变更与版本兼容性
适用场景
- 01仅现代化当前正在修改的代码
- 02规划 Go 指令版本升级
- 03更新弃用或旧式标准库用法
- 04对包或模块执行现代化扫描
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ eduardo-sl/ go-agent-skills --skill go-modernize安装后在 Claude Code 中输入/go-modernize确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/generics.mdgo1.27.mdstdlib-migrations.mdversion-bump.md详细介绍
Go 现代化
Go 在不断演进。为 Go 1.16 编写的代码,不应与面向 Go 1.27 的代码相同。逐步进行现代化改造——先更新 go.mod,再采用新的模式。
切勿采用超出 go.mod 中 go 指令版本的功能。应有计划地提高该指令版本,单独提交,并且仅提高到项目的 CI 和部署镜像实际运行的版本。
按需加载详细参考资料:
references/generics.md— 用类型参数替换interface{}、约束、泛型容器,以及何时不应使用泛型。references/stdlib-migrations.md— slog、errors.Join、slices/maps 辅助函数、按整数范围迭代和迭代器的迁移前后示例。references/go1.27.md— 泛型方法、CutLast、URL/ValuesClone、big.Int.Divide、标准库uuid、json/v2 语义、synctest 和 httptest 新增内容、工具链变更。references/version-bump.md— 提高go指令版本的流程和逐版本检查清单(1.25-1.27),说明可能造成的影响。
仅当以下摘要不足以解答时,才阅读参考文件。
操作模式
- Inline(默认——开发者正在处理其他事项):仅现代化你已在修改的代码行。其他可改进之处只在结尾提及一次,并说明带来的收益;不要修改无关文件——未经请求的大范围改动会淹没真正的改动。如果建议被拒绝,本次会话中不要再提;如果开发者希望记住这一点,可在项目的 Agent 配置文件(CLAUDE.md、AGENTS.md)中添加一行。
- Version bump(提高
go指令版本):即使没有修改源代码,也属于行为变更。在采用任何功能之前,先遵循references/version-bump.md中的前提条件和逐版本影响检查清单。 - Sweep(受托现代化某个包或模块):运行以下流程,并按类别分别提交——
go fix输出、已弃用 API、语言功能、标准库辅助函数、测试——每个类别的构建和测试都必须通过。这些类别彼此独立;如果环境支持并行子 Agent,可为每个类别分配一个任务,并使用单独的分支或工作树。
优先级顺序
时间有限时按以下顺序处理:
- 正确性和安全性 — 使用
errors.Is/As替代==,使用http.NewRequestWithContext,对用户提供的路径使用os.Root,凡是随机性用于保护某项内容时使用crypto/rand,用标准库版本替换已弃用的x/crypto包。 - 可读性 —
any、min/max、按整数范围迭代、slices/maps、cmp.Or、sync.OnceValue、wg.Go、errors.AsType。 - 渐进式改进 — 迭代器、迁移到 slog 日志记录器、
go.mod工具指令,以及移除标准库现已涵盖功能的依赖项。
现代化流程
-
检查
go.mod中的go指令——它限制了可使用的功能。提高该指令版本属于 Version bump 模式,绝不能作为功能变更的附带操作。 -
先运行官方现代化工具——它们会自动查找并修复机械式迁移:
# Go 1.26+ — the modernizers now live in go fix go fix ./... # Go 1.25 and earlier, or a modernizer newer than your toolchain go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./...两者都会原地改写源代码。运行前先提交,并检查差异。如果这两个命令都不可用,请手动应用下表中的改动。
-
扫描下表,查找分析器未涵盖、需要判断的迁移(泛型、迭代器、替换日志记录器),并逐项应用。
-
每组改动完成后,运行
go build ./...和测试套件。
按 Go 版本划分的功能表
| Go 版本 | 功能 | 操作 |
|---|---|---|
| 1.13+ | errors.Is, errors.As | 替换 == 错误比较 |
| 1.13+ | http.NewRequestWithContext | 替换 http.NewRequest |
| 1.16+ | embed | 替换 go-bindata / packr |
| 1.18+ | 泛型 | 替换 interface{} 工具函数 |
| 1.20+ | errors.Join | 替换手动累积错误 |
| 1.21+ | log/slog | 替换 log,以实现结构化日志记录 |
| 1.21+ | slices, maps | 替换手写的切片 / 映射工具函数 |
| 1.21+ | min, max 内置函数 | 替换 math.Min/math.Max(仅支持 float64) |
| 1.21+ | sync.OnceValue, sync.OnceFunc | 替换 sync.Once + 包级变量组合 |
| 1.22+ | 遍历整数 | 替换 for i := 0; i < n; i++ |
| 1.22+ | 每次迭代独立的循环变量 | 删除 v := v 影子副本(仅当 go.mod 声明版本为 1.22+ 时) |
| 1.22+ | math/rand/v2 | 替换 math/rand;删除 rand.Seed |
| 1.22+ | cmp.Or | 替换“首个非零值”if 链 |
| 1.22+ | ServeMux 方法 + 通配符模式 | 替换仅用于 GET /users/{id} 的路由器依赖 |
| 1.23+ | 遍历函数 | 替换基于回调的迭代 |
| 1.23+ | unique.Make | 替换手写的字符串驻留实现 |
| 1.24+ | for b.Loop() | 替换基准测试中的 for range b.N |
| 1.24+ | t.Context() | 替换测试中的 context.Background() |
| 1.24+ | os.Root | 替换手动路径遍历检查 |
| 1.24+ | go.mod tool 指令 | 替换 tools.go 空导入文件 |
| 1.24+ | runtime.AddCleanup | 替换 runtime.SetFinalizer |
| 1.24+ | omitzero 结构体标签 | 当 Go 零值(例如 time.Time{})必须省略时,替换 omitempty |
| 1.24+ | strings.SplitSeq, strings.Lines, FieldsSeq | 当切片仅用于遍历时,替换 Split |
| 1.24+ | crypto/sha3, crypto/hkdf, crypto/pbkdf2 | 替换 golang.org/x/crypto 中的对应实现 |
| 1.24+ | crypto/rand.Text | 替换手写的随机令牌编码器 |
| 1.25+ | testing/synctest | 替换并发测试中的 time.Sleep |
| 1.25+ | sync.WaitGroup.Go | 替换 wg.Add(1) + go func(){defer wg.Done()} |
| 1.25+ | reflect.TypeAssert | 替换 v.Interface().(T)(不分配内存) |
| 1.25+ | 感知容器的 GOMAXPROCS | 移除 go.uber.org/automaxprocs |
| 1.26+ | errors.AsType | 替换 errors.As 和已声明的目标变量 |
| 1.26+ | slog.NewMultiHandler | 替换手写的扇出处理器 |
| 1.26+ | new(expr) | 替换取地址的临时变量 |
| 1.26+ | httputil.ReverseProxy.Rewrite | 替换已弃用且不安全的 Director 钩子 |
| 1.27+ | 泛型方法 | 替换属于某个类型的包级泛型辅助函数(接口必须公开这些方法时除外) |
| 1.27+ | 将提升字段用作字面量键 | 替换 Outer{Inner: Inner{ID: x}} 嵌套写法 |
| 1.27+ | strings.CutLast, bytes.CutLast | 替换 LastIndex + 切片操作 |
| 1.27+ | url.URL.Clone, url.Values.Clone | 替换手写的深拷贝 |
| 1.27+ | big.Int.Divide | 替换 QuoRem + 符号修正 |
| 1.27+ | 标准库 uuid | 对于 v4/v7 ID,替换 google/uuid |
| 1.27+ | encoding/json/v2 | 按调用点采用——语义更严格,参见参考文档 |
| 1.27+ | synctest.Sleep, httptest.NewTestServer | 替换 time.Sleep+Wait 和依赖套接字的测试服务器 |
| 1.27+ | testing/cryptotest.SetGlobalRandom | 替换测试中的 tls.Config.Rand |
迁移示例
上表每一行的迁移前后示例均见
references/stdlib-migrations.md(标准库和语言功能)以及
references/generics.md(类型参数)。测试专用迁移——
t.Context()、b.Loop()、synctest——由 go-test-quality
技能负责。
验证清单
go.mod中的版本与代码库中使用的功能相符- 使用
any或类型参数会更清晰时,不使用interface{} - 使用
log/slog进行结构化日志记录,而非log.Printf - 使用
errors.Join,而非手动拼接错误字符串 - 使用
slices.Contains、slices.SortFunc、maps.Clone替代手写循环 - 适用时使用整数范围循环(
for i := range n) - 使用
http.NewRequestWithContext,而非http.NewRequest - 不使用
sort.Slice——使用slices.SortFunc和cmp.Compare - 使用泛型实现类型安全的容器和实用工具,但不要在简单场景中过度使用
- 评估第三方依赖是否有近期 Go 版本新增的标准库替代方案
- 使用
sync.WaitGroup.Go,而非手动调用Add/Done(Go 1.25+) - 测试使用
t.Context()和for b.Loop()(Go 1.24+) - 使用
go.mod的 tool 指令替代tools.go(Go 1.24+)
评论
支持 Markdown