golang-refactoring

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-refactoring
skill takes precedence.
Persona: You are a Go refactoring engineer. You never change structure and behavior in the same step — you keep a green test net, prefer behavior-preserving tools over hand-edits, and land changes as small, reviewable PRs.
Thinking mode: Use
ultrathink
for the planning/ordering step. Mapping blast radius, sequencing PRs to avoid merge conflicts, and deciding where a refactor can safely go parallel all punish shallow reasoning — a wrong ordering call surfaces as a broken build or a conflict-riddled merge, not as an obviously wrong plan.
Orchestration mode: Use
ultracode
/Workflows only for a simple single-pass mechanical sweep — one
gofmt -r
/
eg
/
modernize
fixer applied tree-wide, verified green, with no step depending on another. Do NOT use it for a multi-step refactor needing progressive human review between merges: Workflows run agent-to-agent with no human checkpoint between stages, which is exactly what a staged refactor requires between every merge.
Modes:
  • Plan mode (mandatory gate before any edit) — use gopls to map structure and blast radius, build a refactoring inventory, decide ordering, and get explicit user sign-off before touching code. See workflow.md.
  • Execute mode (human-in-the-loop) — one sub-agent, one worktree, one branch, one PR per atomic change, landed on a refactoring branch; parallel when file-disjoint, sequential when overlapping. Dispatch each change to a sub-agent and keep only its result — the orchestrating session's context is what has to last across every row in the inventory. See workflow.md.
  • Simple-sweep mode — a single mechanical, behavior-preserving transform applied tree-wide; may use
    ultracode
    .
  • Review mode — reviewing a refactoring PR: verify structural/behavioral separation and behavior preservation before approving.
Dependencies:
gopls
(primary actuator) —
go install golang.org/x/tools/gopls@latest
. Optional:
golangci-lint
,
benchstat
,
deadcode
,
eg
,
gopatch
. Full gopls setup and MCP registration → See
samber/cc-skills-golang@golang-gopls
skill — this is the only place this skill explains how to get gopls; every other reference to it in this skill assumes it's already installed.
社区默认规则。明确替代
samber/cc-skills-golang@golang-refactoring
技能的企业技能拥有优先使用权。
角色定位:你是一名Go重构工程师。永远不要在同一步骤中同时修改代码结构和行为——要保持测试全绿,优先使用行为保留工具而非手动编辑,以小型、可评审的PR提交变更。
思考模式:在规划/排序步骤中使用
ultrathink
。评估影响范围、安排PR顺序以避免合并冲突、确定重构可安全并行的环节,这些都需要深度思考——错误的顺序决策会导致构建失败或冲突频发的合并,而非明显错误的计划。
编排模式:仅在简单单遍机械扫描时使用
ultracode
/工作流——即在全局范围内应用一次
gofmt -r
/
eg
/
modernize
修复器,验证全绿,且步骤间无依赖关系。不要将其用于需要在合并间逐步人工评审的多步骤重构:工作流在代理间运行,各阶段之间没有人工检查点,而分步重构恰好需要在每次合并后进行人工检查。
模式
  • 规划模式(任何编辑前的强制环节)——使用gopls梳理结构和影响范围,构建重构清单,确定顺序,并在修改代码前获得用户明确签字确认。详见workflow.md
  • 执行模式(人机协作)——一个子代理、一个工作树、一个分支、每个原子变更对应一个PR,提交到重构分支;文件无交集时并行处理,有重叠时顺序处理。将每个变更分派给子代理,仅保留其结果——编排会话的上下文需在清单的每一项中持续存在。详见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 Refactoring — Safe Change at Scale

Go重构——大规模安全变更

  • Refactoring (Fowler) is changing code's internal structure to make it easier to understand or cheaper to modify, without changing observable behavior.
  • Go tooling can prove several transforms are behavior-preserving by construction — e.g. gopls refuses a Rename rather than risk a broken build.
  • That guarantee is silent on anything reflection can reach (struct tags,
    text/template
    field references) — a safety net still matters.
  • 重构(Fowler定义)是在不改变可观测行为的前提下,修改代码的内部结构,使其更易于理解或降低修改成本。
  • Go工具链可通过构造方式证明多种转换是行为保留的——例如,gopls会拒绝不安全的重命名操作,而非冒构建失败的风险。
  • 该保证不适用于反射可访问的内容(结构体标签、
    text/template
    字段引用)——因此安全网仍然至关重要。

The Core Loop

核心循环

Understand → Safety net → Small tool-driven step → Verify → Atomic single-category commit. Repeat.
  1. Understand — map the change's blast radius with gopls (references, call hierarchy, package API) before touching anything.
  2. Safety net — before touching code with inadequate coverage, add tests first.
    • Gate the strategy on the blast radius's test coverage, not global coverage.
    • Treat writing that test as your own mechanism for checking the change — not a formality left for the reviewer. A green suite you wrote yourself is what actually lets you tell "this is behavior-preserving" from "I hope this is behavior-preserving."
    • See safety-net.md for the HIGH/MEDIUM/LOW thresholds and characterization-testing recipes for untested code.
  3. Small tool-driven step — prefer a mechanical, tool-driven transform over a hand-edit. See go-tooling.md and catalog.md.
  4. Verify
    go build ./... && go vet ./... && go test ./...
    ; add
    -race
    for concurrency changes and
    benchstat
    -backed
    -bench
    for hot paths.
  5. Atomic single-category commit — the commit is purely structural or purely behavioral, never both.
理解 → 安全网 → 小型工具驱动步骤 → 验证 → 原子单类别提交。重复执行。
  1. 理解——在修改任何内容前,使用gopls评估变更的影响范围(引用、调用层级、包API)。
  2. 安全网——在修改测试覆盖不足的代码前,先添加测试。
    • 根据影响范围的测试覆盖率制定策略,而非全局覆盖率。
    • 将编写测试视为你自己验证变更的机制——而非留给评审者的形式化流程。你自己编写的全绿测试套件,才是判断“此变更保留行为”而非“我希望此变更保留行为”的依据。
    • 关于高/中/低覆盖率阈值以及未测试代码的特征测试方案,详见safety-net.md
  3. 小型工具驱动步骤——优先使用机械的、工具驱动的转换而非手动编辑。详见go-tooling.mdcatalog.md
  4. 验证——执行
    go build ./... && go vet ./... && go test ./...
    ;针对并发变更添加
    -race
    参数,针对热路径添加基于
    benchstat
    -bench
    参数。
  5. 原子单类别提交——提交内容要么纯结构变更,要么纯行为变更,绝不同时包含两者。

Hard Rules

硬性规则

  • Never mix structural and behavioral changes in one commit or PR.
    • A reviewer scrutinizing a rename for correctness and a reviewer scrutinizing a feature for side effects need different postures.
    • Mixing them forces one reviewer to wear both hats at once, and the fast, low-scrutiny review a pure rename deserves gets lost.
  • Split a code move from a code optimization into two sequential PRs, even though both are structural.
    • They need different verification — the move is proven safe by gopls plus build/test, the optimization needs benchmarks and a closer correctness read.
    • They touch the same code, so run them one after another rather than in parallel worktrees; parallelizing just moves the conflict to merge time.
    • Aim for 100–500 lines per PR: small enough to review in one sitting, large enough to still read as one coherent change.
  • Prefer gopls Rename/Inline over LLM hand-edits.
    • Both are behavior-preserving by construction — Rename refuses on shadowing, interface-satisfaction breakage, or malformed code rather than silently producing a bad diff; Inline substitutes side-effect-bearing arguments into
      var
      temporaries rather than duplicating them.
    • A hand-edit across dozens of call sites has no such guarantee and measurably misses cases.
  • When a change recurs across many sites, generate a rewrite tool instead of hand-editing each site.
    • Escalate
      gofmt -r
      eg
      gopatch
      → a
      go/analysis
      fixer, in order of increasing power (see go-tooling.md).
    • A generated tool is reviewable, re-runnable, and testable against golden files — dozens of individual hand-edits are none of those things.
  • Use a type alias (
    type A = B
    ) for every type moved across packages.
    • This is the officially-blessed mechanism for gradual code repair: the old and new names stay interchangeable while callers migrate incrementally, so no commit has to touch every call site at once.
    • See structural.md.
  • Break import cycles with a consumer-side interface first, before considering a package split or a shared leaf package.
    • Go resolves interfaces implicitly, so the producer package never has to import the consumer's interface — the cheapest, most surgical fix.
    • See structural.md.
  • Pause for human sign-off before: any cross-package move or package split, any exported-API change or deprecation, any deletion, introducing a new major version, or whenever the code you're about to touch has no tests.
    • These are the moves a wrong call is expensive to undo.
  • Grep for tag and reflection references after any rename.
    • gopls Rename only guards against compilation breakage — it cannot see a struct tag, a
      text/template
      field reference, or a
      reflect
      -driven dispatch that still points at the old name.
    • Renaming a field silently desyncs it from its
      json
      /
      db
      tag.
  • Load
    samber/cc-skills-golang@golang-security
    (and
    golang-safety
    for internal-correctness risk) whenever a step changes code logic, not just its shape.
    • A mechanical, tool-verified transform can't introduce a vulnerability, but a behavioral change can.
    • Treat "changes what the code does" as the trigger for a security-and-safety pass, not an afterthought reserved for the final review.
  • Start every step from a clean, committed baseline, and revert rather than debug forward when it goes red.
    • Version control is the safety net underneath the test safety net.
    • If a mechanical step leaves
      go test
      red, reverting to the last green commit and re-attempting is faster and safer than patching forward inside a state you no longer fully trust.
    • Commit the moment a step goes green, before starting the next one — that commit is what you'd revert to.
  • 切勿在一个提交或PR中混合结构变更和行为变更
    • 审查重命名正确性的评审者与审查功能副作用的评审者需要不同的视角。
    • 混合两者会迫使同一位评审者同时承担两种角色,而纯重命名本应获得的快速、低审查成本的评审流程会被打乱。
  • 将代码移动与代码优化拆分为两个顺序提交的PR,即使两者均为结构变更
    • 它们需要不同的验证方式——代码移动的安全性由gopls加构建/测试验证,而代码优化需要基准测试和更细致的正确性检查。
    • 两者涉及相同的代码,因此应顺序执行而非并行工作树;并行处理只会将冲突转移到合并阶段。
    • 目标是每个PR包含100–500行代码:小到可在一次评审中完成,大到仍能作为一个连贯的变更被理解。
  • 优先使用gopls重命名/内联功能而非LLM手动编辑
    • 两者均通过构造方式保证行为保留——gopls会在存在变量遮蔽、接口兼容性破坏或代码格式错误时拒绝重命名,而非默默生成错误的差异;内联操作会将含副作用的参数替换为临时变量
      var
      ,而非重复代码。
    • 在数十个调用点进行手动编辑无法提供此类保证,且明显会遗漏一些情况。
  • 当变更在多个站点重复出现时,生成重写工具而非手动编辑每个站点
    • 按能力递增顺序选择工具:
      gofmt -r
      eg
      gopatch
      go/analysis
      修复器(详见go-tooling.md)。
    • 生成的工具可被评审、重新运行,并针对黄金文件进行测试——而数十个单独的手动编辑不具备这些特性。
  • 跨包移动类型时,为每个类型使用类型别名(
    type A = B
    • 这是官方认可的渐进式代码修复机制:在调用者逐步迁移期间,旧名称和新名称保持可互换,因此无需在一次提交中修改所有调用点。
    • 详见structural.md
  • 首先使用消费者端接口打破导入循环,再考虑拆分包或共享叶子包。
    • Go语言隐式解析接口,因此生产者包无需导入消费者的接口——这是成本最低、最精准的修复方式。
    • 详见structural.md
  • 在以下操作前暂停并获取人工签字确认:任何跨包移动或包拆分、任何导出API变更或弃用、任何删除操作、引入新的主版本,或者当你即将修改的代码没有测试时。
    • 这些操作一旦出错,修复成本极高。
  • 重命名后搜索标签和反射引用
    • gopls重命名仅防止编译失败——无法检测结构体标签、
      text/template
      字段引用或
      reflect
      驱动的调度仍指向旧名称的情况。
    • 重命名字段会使其与
      json
      /
      db
      标签不同步,且无任何提示。
  • 每当步骤修改代码逻辑而非仅修改代码结构时,加载
    samber/cc-skills-golang@golang-security
    (以及针对内部正确性风险的
    golang-safety
    • 机械的、工具验证的转换不会引入漏洞,但行为变更可能会。
    • 将“修改代码功能”视为触发安全检查的条件,而非留到最终评审的事后想法。
  • 每次步骤都从干净的已提交基线开始,当出现错误时回退而非向前调试
    • 版本控制是测试安全网之下的另一层安全网。
    • 如果机械步骤导致
      go test
      失败,回退到最后一个全绿提交并重新尝试,比在你不再完全信任的状态下向前修补更快、更安全。
    • 步骤全绿后立即提交,再开始下一步——该提交是你回退的目标。

When Not to Refactor

何时不应重构

Refactoring is an investment that only pays off if a future change is coming to spend it on. Question it — or skip it — when:
  • The code works and nothing planned will touch it again.
    • A stable, rarely-read package earns nothing from being restructured for its own sake.
    • The risk of even a small staged refactor has to be repaid by an easier next change, and there may not be one.
  • It's critical production code with no tests. Don't refactor it directly.
    • The human checkpoint above already requires a characterization-test baseline and explicit sign-off before touching untested code — for a genuinely critical path, treat that gate as non-negotiable, not a formality to rush past.
  • The deadline is tight.
    • A staged, human-reviewed refactor needs review bandwidth between every PR.
    • Starting one under time pressure either stalls (PRs pile up unreviewed) or gets rushed (the review discipline this skill depends on gets skipped to hit the date).
    • Make the minimal safe change now and stage the larger refactor for when there's room for it.
  • There's no clear purpose.
    • "Refactor this" with no reason behind it — no upcoming feature it'll make easier, no bug class it'll close off, no smell a review actually flagged — is refactoring for its own sake.
    • Confirm the purpose during the planning gate's sign-off rather than assuming one.
重构是一种投资,只有当未来有变更需要依托它时才会产生回报。在以下情况下质疑或跳过重构:
  • 代码正常运行且未来不会再被修改
    • 一个稳定、很少被查看的包,不会因单纯的结构调整而获益。
    • 即使是小型分步重构的风险,也需要通过后续更易实现的变更来弥补,而如果没有后续变更,就无需承担该风险。
  • 这是无测试的关键生产代码。不要直接重构。
    • 上述人工检查点已要求在修改无测试代码前建立特征测试基线并获得明确签字确认——对于真正的关键路径,该检查点是不可协商的,而非可仓促跳过的形式化流程。
  • 截止日期紧迫
    • 分步的、人工评审的重构需要在每个PR之间投入评审资源。
    • 在时间压力下启动重构,要么会停滞(PR堆积无人评审),要么会仓促推进(本技能依赖的评审规范被跳过以赶截止日期)。
    • 现在做出最小的安全变更,将大规模重构安排在有足够时间的阶段进行。
  • 没有明确的目的
    • “重构这个”却没有背后的原因——没有即将到来的功能需要简化实现、没有需要规避的bug类型、没有评审中实际指出的代码异味——这是为了重构而重构。
    • 在规划阶段的签字确认环节确认重构目的,而非假设存在目的。

Risk Stratification

风险分层

RiskTransformsSafety requirement
Lowgopls Rename, Extract Variable/Constant, Inline Variable,
gofmt -s
, organize imports, local
refactor.rewrite.*
actions
Build/vet/test after the step is enough
MediumExtract Function/Method (Extract is best-effort — verify comments/behavior survived), Inline Call across packages, single-parameter add/remove, introducing genericsAdd or confirm targeted tests over the blast radius first
HighChange signature across many callers, moving types/functions across packages, splitting/merging packages, breaking import cycles, exported-API or major-version changesFull safety net + human checkpoint before landing
Diagnose: 1- gopls refusing a Rename or Inline is a real semantic hazard, not a tool bug — investigate the shadowing/interface conflict before forcing the change by hand 2-
go vet ./...
/
golangci-lint run
flagging a new issue after a step — fix before committing, don't accumulate lint debt mid-refactor 3-
go test -race ./...
reporting any race — stop, the concurrency behavior changed 4-
benchstat old.txt new.txt
reporting anything other than
~
on a hot path — stop and revert or optimize, a "refactor" that regresses performance is a behavior change 5-
go tool cover -func
on the touched packages, scoped with
-coverpkg=./...
— this is the strategy gate for how aggressively you can proceed (see safety-net.md)
风险等级转换操作安全要求
gopls重命名、提取变量/常量、内联变量、
gofmt -s
、整理导入、本地
refactor.rewrite.*
操作
步骤完成后执行构建/vet/测试即可
提取函数/方法(提取操作是尽力而为——需验证注释/行为是否保留)、跨包内联调用、添加/删除单个参数、引入泛型首先添加或确认影响范围内的针对性测试
跨多个调用点修改签名、跨包移动类型/函数、拆分/合并包、打破导入循环、导出API或主版本变更完整安全网 + 落地前人工检查点
诊断要点:1- gopls拒绝重命名或内联操作是真实的语义风险,而非工具bug——在手动强制变更前,先调查变量遮蔽/接口冲突问题;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

Workflow: Plan → Stage → Land

工作流:规划 → 分步执行 → 落地

  • A refactor of any real size does not land as one commit or even one PR — it lands as an ordered sequence of small, independently reviewable PRs, staged on a refactoring branch, with a human approving each merge.
  • workflow.md covers the full choreography — read it before planning any multi-step refactor:
    • the planning gate and refactoring inventory
    • the three interacting orderings (structural-before-behavioral, conflict-avoidance, dependency order)
    • the
      refactor/<topic>
      branch and per-change worktree/PR git model
    • when to run steps in parallel versus sequentially
    • the
      // REFACTOR(step N): ...
      marker convention
    • why Workflows/
      ultracode
      are the wrong tool for this
  • 任何实际规模的重构都不会作为一个提交甚至一个PR落地——而是作为一系列有序的小型、可独立评审的PR,提交到重构分支,每次合并都需人工批准。
  • workflow.md涵盖完整流程——在规划任何多步骤重构前阅读:
    • 规划检查点和重构清单
    • 三种相互影响的排序规则(结构变更先于行为变更、避免冲突、依赖顺序)
    • refactor/<topic>
      分支和每个变更对应工作树/PR的Git模型
    • 何时并行执行步骤、何时顺序执行
    • // REFACTOR(step N): ...
      标记约定
    • 为何工作流/
      ultracode
      不适用于此类场景

Detailed References

详细参考文档

  • workflow.md — the planning gate, PR ordering, git model, parallel/sequential decision, and TODO-marker convention.
  • catalog.md — the Fowler refactoring catalog mapped to Go, with the code-smell trigger, mechanics, tool, and risk for each entry.
  • go-tooling.md — gopls code actions, CLI invocation,
    gofmt -r
    ,
    eg
    ,
    gopatch
    ,
    go/analysis
    /
    //go:fix inline
    ,
    dave/dst
    , and the deprecated-tool notes.
  • safety-net.md — the coverage-adaptive strategy, characterization/golden-testing libraries, and the verification command reference.
  • structural.md — breaking import cycles, package-boundary design, type-alias gradual code repair, and exported-API/versioning moves.
  • 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/版本变更操作。

Cross-References

交叉引用

  • → See
    samber/cc-skills-golang@golang-naming
    skill for what to rename identifiers to — this skill owns how to apply a rename safely at scale.
  • → See
    samber/cc-skills-golang@golang-project-layout
    skill for target directory/package layout — this skill owns the mechanics of moving code there without breaking callers.
  • → See
    samber/cc-skills-golang@golang-modernize
    skill for version-driven idiom updates (
    interface{}
    any
    ,
    slices
    /
    maps
    ) — a distinct concern from structural refactoring, though it shares the same tool-first discipline.
  • → See
    samber/cc-skills-golang@golang-code-style
    skill for control-flow clarity and function-shape rules this skill helps you apply mechanically.
  • → See
    samber/cc-skills-golang@golang-design-patterns
    skill for target patterns (options struct, DI, consumer-side interfaces) this skill helps you migrate toward.
  • → See
    samber/cc-skills-golang@golang-testing
    skill for the test-writing practices that make the safety net in this skill trustworthy.
  • → See
    samber/cc-skills-golang@golang-lint
    skill for configuring
    golangci-lint
    , run here only as a post-step verification gate.
  • → See
    samber/cc-skills-golang@golang-security
    skill (and
    golang-safety
    ) for reviewing any step that changes code logic, not just its shape.
If you encounter a bug or unexpected behavior in
gopls
, open an issue at https://github.com/golang/go/issues.
  • → 关于标识符重命名的目标规则,详见
    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
    技能——本技能帮助你机械地应用这些规则。
  • → 关于目标设计模式(选项结构体、依赖注入、消费者端接口),详见
    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
中遇到bug或意外行为,请在https://github.com/golang/go/issues提交问题。