关于 golang-refactoring
面向 Go 编码 Agent 的安全重构 skill,强调先规划并取得批准,再以小步、工具驱动且可验证的方式修改代码。通过分离结构与行为变更、测试和人工检查点,支持可审查的渐进式重构。
主要功能
- 01重构前绘制影响范围并取得批准
- 02结构变更与行为变更分开提交
- 03优先使用 gopls 等工具执行变换
- 04按风险配置测试和人工检查点
- 05将大型重构拆为顺序 PR
适用场景
- 01规划跨包移动或包拆分
- 02安全地批量重命名符号
- 03为缺少测试的代码建立安全网
- 04审核重构 PR 是否保持行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-refactoring安装后在 Claude Code 中输入/golang-refactoring确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/catalog.mdgo-tooling.mdsafety-net.mdstructural.mdworkflow.md详细介绍
社区默认规则。 明确取代
samber/cc-skills-golang@golang-refactoring技能的公司技能优先。
**角色:**你是一名 Go 重构工程师。你绝不会在同一步中同时更改结构和行为——你会保持测试网全绿,优先使用保持行为不变的工具,而非手动编辑,并以小型、易于审查的 PR 提交更改。
**思考模式:**在规划和排序步骤中尽可能彻底地推理——梳理影响范围、安排 PR 顺序以避免合并冲突,并判断哪些重构可以安全并行;浅层推理在这些方面都会带来风险,因为排序决策错误会导致构建失败或合并冲突,而不会显然呈现为错误计划。在 Claude Code 中,使用 ultrathink 显式触发扩展思考。
编排模式:仅将 ultracode/Workflows 用于简单的单次机械式全面处理——在整个代码树中应用一次 gofmt -r/eg/modernize 修复器,验证结果全绿,且各步骤之间没有依赖关系。不要将其用于需要在合并之间逐步进行人工审查的多步骤重构:Workflows 在阶段之间通过 Agent 对 Agent 运行,不设人工检查点;而分阶段重构恰恰要求每次合并之间都设置检查点。
模式:
- 计划模式(任何编辑前的强制关卡)——使用 gopls 梳理结构和影响范围,建立重构清单,确定执行顺序,并在改动代码前取得用户的明确批准。参见 workflow.md。
- 执行模式(人工参与)——每项原子变更使用一个子 Agent、一个工作树、一个分支和一个 PR,并将其合并到重构分支;文件互不重叠时并行处理,有重叠时按顺序处理。将每项变更分派给子 Agent,并且只保留其结果——编排会话的上下文必须贯穿清单中的每一项。参见 workflow.md。
- 简单全面处理模式——在整个代码树中应用单次机械式、保持行为不变的转换;可以使用
ultracode。 - 审查模式——审查重构 PR:批准前,确认结构变更与行为变更相互分离,且行为保持不变。
**提问:**此技能中的批准关卡(计划模式的初始批准,以及下文每个重构中途检查点)都必须通过环境的提问工具发起,不能只用读者可能一眼略过的纯文本提出——重构正是「默认视为同意」这种做法难以挽回的工作流。因此,在下文每个关卡都再次说明「通过提问工具询问」,是有意为之,并非套话。这些关卡用于批准不可逆决策,不是随意澄清问题。
依赖项:gopls(主要执行工具)——go install golang.org/x/tools/gopls@latest。可选:golangci-lint、benchstat、deadcode、eg、gopatch。完整的 gopls 设置和 MCP 注册 → 参见 samber/cc-skills-golang@golang-gopls 技能——此技能只有这里会说明如何获取 gopls;其他所有提及均假设已安装 gopls。
Go 重构——大规模安全变更
- 重构(Fowler)是指更改代码的内部结构,使其更易于理解或更便于修改,而不改变可观察到的行为。
- Go 工具可以从构造上证明多种转换能够保持行为不变——例如,为避免破坏构建,gopls 会拒绝执行 Rename。
- 这一保证不涵盖反射能够访问的任何内容(结构体标签、
text/template字段引用)——因此,仍然需要安全网。
核心循环
理解 → 安全网 → 由工具驱动的小步修改 → 验证 → 单一类别的原子提交。 循环往复。
- 理解 — 修改前,使用 gopls(引用、调用层次、包 API)梳理变更的影响范围。
- 安全网 — 覆盖率不足的代码修改前,先添加测试。
- 根据_影响范围_的测试覆盖率来决定策略,而非全局覆盖率。
- 将编写该测试视为你检查变更的手段,而非留给审查者走过场。自己编写的测试套件通过后,才能真正判断“这是保留行为的修改”,而不是“我希望这是保留行为的修改”。
- 关于未测试代码的 HIGH/MEDIUM/LOW 阈值和特征测试方案,请参阅 safety-net.md。
- 由工具驱动的小步修改 — 优先采用机械化、由工具驱动的转换,而非手动编辑。请参阅 go-tooling.md 和 catalog.md。
- 验证 —
go build ./... && go vet ./... && go test ./...;并发修改需添加-race,热点路径需使用由benchstat支持的-bench。 - 单一类别的原子提交 — 提交只能纯粹是结构性修改或行为性修改,不能两者兼有。
硬性规则
- 绝不在同一个提交或 PR 中混合结构性变更和行为变更。
- 审查重命名是否正确的审查者,与审查功能是否有副作用的审查者,需要采用不同的审查方式。
- 将两者混在一起,就迫使同一位审查者同时承担这两种角色,也会让纯重命名本应享有的快速、低审查强度流程荡然无存。
- 将代码移动和代码优化拆分为两个连续的 PR,即使两者都是结构性变更。
- 两者需要不同的验证方式:移动操作通过 gopls 加构建和测试来证明安全;优化则需要基准测试和更仔细的正确性审查。
- 它们会修改相同的代码,因此应依次进行,而不是放在并行工作树中;并行操作只会把冲突推迟到合并时。
- 每个 PR 以 100–500 行为宜:小到可以一口气审完,大到仍然构成一项连贯的变更。
- 优先使用 gopls Rename/Inline,而不是手动用 LLM 编辑。
- 两者从设计上就能保留行为:遇到变量遮蔽、接口实现失效或代码格式错误时,Rename 会拒绝操作,而不是悄悄生成糟糕的差异;Inline 会将有副作用的实参替换为
var临时变量,而不是重复执行它们。 - 手动编辑数十个调用点没有这类保障,而且可量化地会遗漏情况。
- 两者从设计上就能保留行为:遇到变量遮蔽、接口实现失效或代码格式错误时,Rename 会拒绝操作,而不是悄悄生成糟糕的差异;Inline 会将有副作用的实参替换为
- 当某项变更会在许多位置重复出现时,应生成重写工具,而不是逐处手动编辑。
- 按功能递增的顺序依次使用
gofmt -r→eg→gopatch→go/analysis修复器(参见 go-tooling.md)。 - 生成的工具可审查、可重复运行,也可通过黄金文件进行测试;数十处手动编辑则都不具备这些特点。
- 按功能递增的顺序依次使用
- 将类型移至其他包时,一律使用类型别名(
type A = B)。- 这是官方认可的_渐进式代码修复_机制:迁移期间,新旧名称保持互换,因此无需在同一个提交中修改所有调用点。
- 参见 structural.md。
- 应先通过消费方接口打破导入循环,再考虑拆分包或创建共享的叶子包。
- Go 会隐式解析接口,因此生产方包完全不必导入消费方的接口,这是成本最低、最精准的修复方式。
- 参见 structural.md。
- 以下操作开始前,先暂停并取得人工签字确认:任何跨包移动或包拆分、任何导出 API 变更或弃用、任何删除、引入新的主版本,或即将修改的代码没有测试时。
- 这些操作一旦判断错误,撤销的代价很高。
- 任何重命名后,都要搜索标签和反射引用。
- gopls Rename 只能防止_编译_失败,无法发现仍指向旧名称的结构体标签、
text/template字段引用或由reflect驱动的分派。 - 重命名字段会使其与
json/db标签悄然失去同步。
- gopls Rename 只能防止_编译_失败,无法发现仍指向旧名称的结构体标签、
- 每当某一步会改变代码逻辑,而不只是代码形式时,都要加载
samber/cc-skills-golang@golang-security(存在内部正确性风险时,也要加载golang-safety)。- 机械且经工具验证的转换不会引入漏洞,但行为变更可能会。
- 应将“改变代码的行为”视为进行安全性审查的触发条件,而不是留到最终审查才考虑的事后补充。
- 每一步都从干净且已提交的基线开始;如果测试失败,应回退,而不是继续在错误状态上调试。
- 版本控制是测试安全网之下的安全保障。
- 如果某个机械步骤导致
go test失败,回退到最近一次通过的提交再重试,比在一个已不再完全可信的状态上继续打补丁更快、更安全。 - 每一步通过后立即提交,再开始下一步;需要回退时,就回退到这个提交。
何时不应重构
重构是一项投资,只有未来有变更需要它时才能获得回报。遇到以下情况时,请质疑是否要重构,或者跳过重构:
- 代码运行正常,而且没有计划中的变更会再次涉及它。
- 稳定且很少有人阅读的包,不会因为单纯重组结构而获得任何收益。
- 即使是小规模的分阶段重构,也必须通过让下一次变更更容易来收回风险成本,而下一次变更可能根本不会到来。
- 这是没有测试的关键生产代码。不要直接重构它。
- 上述人工检查点已经要求,在触碰未经测试的代码之前,先建立特征测试基线并获得明确批准;对于真正关键的路径,应将这道关卡视为不可妥协的要求,而不是匆忙略过的形式流程。
- 截止日期很紧。
- 分阶段、经人工审查的重构要求每个 PR 之间都有足够的审查时间。
- 在时间压力下启动重构,要么会陷入停滞(PR 堆积,无人审查),要么会被匆忙完成(为了赶上期限而跳过这项技能所依赖的审查规范)。
- 现在先做最小且安全的变更,把较大的重构留到时间充裕时再进行。
- 没有明确的目的。
- 如果“重构这个”背后没有理由——没有能因此更容易实现的即将到来的功能、没有能因此消除的错误类别,也没有审查时实际指出的问题——那就是为了重构而重构。
- 在规划关卡的批准过程中确认目的,不要想当然地认定存在目的。
风险分级
| 风险 | 转换操作 | 安全要求 |
|---|---|---|
| 低 | gopls Rename、Extract Variable/Constant、Inline Variable、gofmt -s、整理导入、局部 refactor.rewrite.* 操作 | 步骤完成后执行 build/vet/test 即可 |
| 中 | Extract Function/Method(Extract 尽力而为——请确认注释和行为均得以保留)、跨包 Inline Call、添加或移除单个参数、引入泛型 | 先添加或确认覆盖影响范围的针对性测试 |
| 高 | 修改许多调用方使用的签名、在包之间移动类型/函数、拆分/合并包、打破导入循环、导出 API 或主版本变更 | 完整的安全保障 + 合并前的人工检查点 |
诊断: 1- gopls 拒绝 Rename 或 Inline,说明存在真正的语义风险,并非工具错误——在手动强行变更之前,先调查遮蔽或接口冲突 2- 某个步骤之后,go vet ./... / golangci-lint run 标记出新问题——请在提交前修复,不要在重构过程中累积 lint 技术债 3- go test -race ./... 报告任何竞争条件——停止,说明并发行为发生了变化 4- benchstat old.txt new.txt 在热点路径上报告的结果不是 ~——停止并回退或优化;导致性能退化的“重构”属于行为变更 5- 对涉及的包运行 go tool cover -func,并通过 -coverpkg=./... 指定范围——这是决定你可以多激进地推进的策略关卡(参见 safety-net.md)
工作流:规划 → 分阶段实施 → 合并
- 任何有一定规模的重构都不应以单个提交甚至单个 PR 的形式合并——而应在重构分支上按顺序分成一系列小型、可独立审查的 PR,并由人工逐个批准合并。
- workflow.md 介绍了完整的协作流程——规划任何多步骤重构之前,请先阅读:
- 规划关卡和重构清单
- 三种相互作用的排序方式(结构性变更优先于行为性变更、避免冲突、依赖顺序)
refactor/<topic>分支,以及按变更创建 worktree/PR 的 Git 模型- 何时并行执行步骤,何时按顺序执行
// REFACTOR(step N): ...标记约定- 为什么 Workflows/
ultracode不适合用于此目的
详细参考
- workflow.md — 规划关卡、PR 顺序、Git 模型、并行/顺序决策,以及 TODO 标记约定。
- catalog.md — 映射到 Go 的 Fowler 重构目录,涵盖每个条目的代码异味触发条件、操作步骤、工具和风险。
- go-tooling.md — gopls 代码操作、CLI 调用、
gofmt -r、eg、gopatch、go/analysis///go:fix inline、dave/dst,以及已弃用工具的说明。 - safety-net.md — 覆盖率自适应策略、特征化/黄金测试库,以及验证命令参考。
- structural.md — 打破导入循环、包边界设计、使用类型别名逐步修复代码,以及导出 API/版本管理方面的调整。
交叉引用
- → 如需了解标识符应重命名为_什么_,请参阅
samber/cc-skills-golang@golang-naming技能;本技能负责如何在大规模重命名时安全地应用重命名。 - → 如需了解目标目录/包布局,请参阅
samber/cc-skills-golang@golang-project-layout技能;本技能负责如何将代码移至目标位置而不破坏调用方。 - → 如需了解由版本驱动的惯用写法更新(
interface{}→any、slices/maps),请参阅samber/cc-skills-golang@golang-modernize技能;这与结构重构是不同的关注点,但同样遵循工具优先的原则。 - → 如需了解本技能帮助你通过机械方式应用的控制流清晰度和函数形态规则,请参阅
samber/cc-skills-golang@golang-code-style技能。 - → 如需了解本技能帮助你迁移采用的目标模式(选项结构体、DI、消费者侧接口),请参阅
samber/cc-skills-golang@golang-design-patterns技能。 - → 如需了解能让本技能中的安全网值得信赖的测试编写实践,请参阅
samber/cc-skills-golang@golang-testing技能。 - → 如需了解
golangci-lint的配置,请参阅samber/cc-skills-golang@golang-lint技能;此处仅将其作为后置验证关卡运行。 - → 如需审查任何会改变代码逻辑(而不仅仅是代码形态)的步骤,请参阅
samber/cc-skills-golang@golang-security技能(以及golang-safety)。
如果你在 gopls 中遇到错误或意外行为,请在 https://github.com/golang/go/issues 提交 issue。