Refactoring
Refactoring 是每个快速 RED-GREEN-REFACTOR 增量中,在适用重构时的最后一步。应在 GREEN 建立通过的行为测试基线后进行评估。不要在每次重构前后运行自动化变异测试工具;变异测试在阶段工作基本准备就绪、可提交 PR 时,对完成阶段进行一次验证。
由于自动化变异证据被有意推迟,重构期间基线强度尚未经过变异工具验证。保持每次重构小、严格保持行为,并在现有预言机下保持绿色;最终门禁验证累积结果。
本技能安全地实施有界、保持行为的改进。使用 improve-codebase-architecture 发现并排序架构候选,然后使用 codebase-design 设计选定模块契约,再返回此处实施。如果切片参与选定的全路径削减计划,无论是过渡性还是终态削减,reduce-system-complexity 负责账本和门禁状态;本技能仅作为适用的次要重构评估使用。
何时重构
- 在 GREEN 或其他通过的相应保持基线之后评估
- 在评估前、首次编辑前,以及任何拒绝编辑的回复前,自行运行适用测试。拒绝请求,或将所有候选分类为 Skip,仍然是一次评估,而不基于任何测试运行的评估毫无基础
- 在首次编辑前,列出每个候选变更并标记为 Critical、High、Nice 或 Skip
- 无论你更改了全部、一项还是什么都没改,最终回复都必须携带该标记列表,包括 Skip 项,每个候选一行:
Critical: <内容与原因>、High: <内容与原因>、Nice: <内容与原因>、Skip: <内容与原因>,并以Decision: <你做了什么或没做什么>行收尾。变更摘要不是评估,未标记的建议也不是。未陈述的评估就是未做出的评估 - 当每个候选都是 Skip 时,用这些词说明,并且不做任何更改
- 只有重构能改进代码时才进行
- 在重构前建立经过验证、可恢复的基线;仅在用户明确批准后提交
建立可恢复基线 - 原因
重构前拥有可用基线:
- 允许在重构破坏时回退
- 为实验提供安全网
- 降低重构风险
- 在用户授权提交时,能在 git 历史中显示清晰分离
如果无法在不创建提交的情况下安全恢复基线,停止并请求批准,而不是隐式提交。
工作流:
- BASELINE:适用测试通过,和/或被保持的行为与保证有相应证据
- CHECKPOINT:记录基线和保持证据。仅在用户明确批准时创建基线提交
- REFACTOR:在
tdd技能的规范快速反馈策略下,以小步骤改进结构。从干净基线开始,优先使用经过验证、仓库自有的图完整监视器;仅在已安装版本/配置通过规范干净启动实时证明时,使用 diff 选择的 Vitest watch,否则重复受影响的一次性运行。在 monorepo 中使用根图,使传递消费者保持符合条件 - VERIFY:每一步后保持聚焦和受影响测试以及其他相应证据为绿色;不要每次编辑后重跑完整套件
- CHECKPOINT:呈现已验证的重构,加上标记的
Critical:/High:/Nice:/Skip:评估行和收尾Decision:行;变更摘要不能结束此步骤。仅在用户明确批准后提交 - PRE-PR GATE:当阶段基本准备就绪可提交 PR 时,在有意义处对累积范围运行一次变异测试,或记录明确的
N/A加相应替代证据;在该门禁中处理有价值的存活变异体
完整切片
围绕一个有界所有者或行为路径交付小型完整变更。一起更新受影响契约、适配器和消费者;先迁移所有处理器再迁移所有服务的横向迁移通常会留下半转换路径。保留现有行为预言机作为保持证据,并单独识别新机制所需的测试。当声称简化时,展示移除了什么包装器、重复决策、转换或依赖;仅提取可能改善理解而不减少机制。仅为已证明的发布约束添加兼容脚手架,并带有所有者和移除条件;对于全路径削减声明使用 reduce-system-complexity。
优先级分类
| 优先级 | 操作 | 示例 |
|---|---|---|
| Critical | 立即修复 | 改变行为的变异、同一业务规则的分歧副本、掩盖高风险路径的控制流 |
| High | 本次会话 | 魔法数字(每条规则一个常量;当前仅共享一个值的两条规则各自拥有自己的常量)、不清晰命名、协调多个职责的函数 |
| Nice | 稍后 | 次要命名、单次使用辅助函数 |
| Skip | 不要更改 | 已经干净的代码 |
DRY = 知识,而非代码
抽象时机:
- 相同业务概念(语义含义)
- 如果需求变化会一起变化
- 明显为何归在一起
保持分离时机:
- 看起来相似但概念不同(结构性)
- 会独立演化
- 耦合会令人困惑
当前共享主体或数字的两条规则是巧合,不是共享知识。保持分离意味着每条规则保留自己的主体和自己的命名常量,即使两个值相等;绝不要用一条规则定义另一条规则,也绝不要让两者通过共享谓词辅助函数路由,因为那会让一次编辑静默改变两条业务规则。当请求要求你将此类相似物折叠为一个时,不要这样做。即使你没有更改任何内容,也请在回答前运行适用测试——拒绝是一次评估,需要与编辑相同的绿色基线。然后命名两条不同规则,说明任一变化时它们会漂移,保留两个定义,并只提供不合并它们的更改。
评估示例 - 将此形状写入你的回复
// After GREEN establishes a passing behavior-test baseline:
const planBatch = (batch: Batch): PlannedBatch => {
const itemSlots = batch.items.reduce((sum, item) => sum + item.quantity, 0);
const bufferSlots = itemSlots > 50 ? 0 : 6;
return { ...batch, plannedSlots: itemSlots + bufferSlots, bufferSlots };
};
// ASSESSMENT:
// ⚠️ High: Magic numbers 50, 6 → extract constants
// ✅ Skip: Structure is clear enough
// DECISION: Extract constants only
新行为需要证据
不要在没有失败测试或其他仓库授权的验收证明要求时添加新行为。保持行为的重构可以在没有新 RED 测试的情况下更改行,只要相应保持证据保持绿色。在 PR 就绪时,在 meaningful 处对累积范围使用变异证据,在不可用处使用明确的替代证据;绝不要发明结构性变异体。
❌ 推测性添加:
- “以防万一”逻辑
- 尚未需要的功能
- 仅为想象未来灵活性编写的抽象
- 没有可接受契约的新错误行为
✅ 正确方法:不要添加推测性行为。如果需要,编写要求它的失败测试,然后实现。
现有未测试代码并不被证明是推测性或死代码。在移除分支前,刻画其可观察行为,检查每个调用者和可达路径,并解决行为权威。仅当证据显示不可达或可接受契约明确退役它时删除,然后保持保留/回归检查绿色。
// ❌ WRONG - Speculative error handling (no test demands this)
if (items.length === 0) {
throw new Error('Empty cart'); // No test for this path!
}
// ✅ CORRECT - Test-driven error handling
// First: write a test that expects this behavior
// Then: implement the guard clause to make it pass
何时不要重构
不要重构当:
- ❌ 当前结构未阻碍手头工作(足够干净的可用代码无需重构)
- ❌ 推测性通用性——为尚不存在的需求重构
- ❌ 会改变行为(那是功能,不是重构)
- ❌ 过早优化
- ❌ 代码对当前阶段“足够好”
- ❌ 仅为可测试性提取——如果移入单独文件的唯一原因是“这样我们可以单元测试它”,保持内联。消费函数已有覆盖此代码的行为测试。为可读性、DRY(相同知识在多处使用——见上文“DRY = 知识,而非代码”)或关注点分离提取,绝不要仅为可测试性提取。
记住:重构应改进代码结构而不改变行为。
重构提交信息
当用户批准重构提交时,使用类似以下聚焦信息:
refactor: extract scenario validation logic
refactor: simplify error handling flow
refactor: rename ambiguous parameter names
格式:refactor: <更改了什么>
注意:使用提交时,重构提交不应与功能提交混合。
重构检查清单
- [ ] 现有行为测试通过;测试编辑未隐藏行为变更
- [ ] 重构期间聚焦/受影响测试保持绿色,且仓库定义的完整非监视 PR 测试门禁在 PR 前通过
- [ ] 如果重构阶段已准备就绪可提交 PR,则在 meaningful 处对累积范围审查一次变异结果,或记录明确
N/A加相应替代证据 - [ ] 未添加计划外的面向消费者 API;内部或临时契约遵循选定设计和兼容计划
- [ ] 代码比之前更可读
- [ ] 任何提交均已明确批准并与功能分离
- [ ] 建立了经过验证、可恢复的基线;仅在批准时创建基线提交
- [ ] 未添加推测性代码
- [ ] 在通过的保持证据的信心和保真度内行为不变