重构 — 一次结构实验
重构是在保持可观察行为不变的前提下改变结构。它执行一个调用者选择的转换,并报告结果。
Prompt
重构 billing-service/internal/retry/backoff.go:将 RetryRequest 中的指数退避计算提取为一个独立函数,不改变其他行为。记录基线,在修改前后运行 go test ./internal/retry/...,并报告差异摘要、命令、结果以及任何未检查的内容。
工作原理
- 报告应指明被保留的行为,并引用修改前后运行的
go test ./internal/retry/...。 git diff --stat应仅涉及internal/retry/backoff.go,绝不涉及无关文件。- 当更改的表面产生输出时,应捕获黄金输出哈希并进行逐字节比较,例如在修改前后运行
sha256sum。 - 报告中的“行为未检查”列表即使在空时也应在输出中存在,并指明门控跳过的任何表面。
过程
- 明确被保留的行为、聚焦的验收表面,以及为其调用者存在的具体结构问题。重用调用者的领域术语和已接受的行为示例;在更改过程中保持它们的含义不变。
- 记录一个诚实的基线,包括任何可复现的环境故障。对于比较可执行行为的评估,在修改前固定起始源码并构建其基线;保留该二进制文件和比较输入。使用这些输入和相同的工具链对候选版本进行比较。这不会为普通重构增加可执行比较的仪式。
- 应用一个有界的转换:提取、重命名、内联、简化、封装、移动或删除死代码。根据调用者必须理解的内容以及领域规则必须更改的位置来判断结果,而非仅依据文件大小。
- 运行聚焦检查以及由更改表面证明合理的最小包级回归检查。
- 返回差异摘要、命令、结果以及未检查的行为。
不要将新发现的行为修复与结构变更混合。红色结果是提供给调用者的证据;此技能不会自动回退、收窄、重试、提交、验证或引导后续工作。
职责与接口成本
在添加接口或拆分模块之前,检查代表性的调用者。计算它们必须协调的概念:必需的设置、顺序、状态、错误处理以及重复的领域规则。一个有用的边界将内聚的规则置于一个所有者之下,并允许调用者请求一个结果而无需重现该规则。拒绝那些仅增加一个新名称或将相同协调推给调用者的包装器。当没有具体的调用者问题需要更改时,现有边界已足够。
对提取的操作和类型使用调用者的词汇。改变行为的命名歧义应归入现有领域定义;仅当该区分需要处理时才咨询 Domain。重命名公共符号、持久化字段或协议值是一项兼容性变更,除非接受的范围内另有规定。
当转换需要一个 seam(提取边界、接口或模块拆分),并且存在多个候选 seam 时,在切割之前先探测。在可丢弃的隔离环境中运行探测(调用者策略允许的分支、工作区或复制的树):粗略地引入 seam,观察它带来的影响——签名变动、导入循环、测试重写——然后丢弃探测,仅保留知识。停止条件:最多两次探测;如果第二个候选 seam 也回弹,则将两个发现都报告给调用者,而不要尝试第三次。直接将第一个可想象的 seam 切入工作树是过早 seam 失败模式:错误的边界会固化,因为现在回退它的成本高于忍受它。
中立性门控
“行为保持”是一个需要执行的声明,而非断言。通过行为相同的证据来门控转换:
- 聚焦检查和包级回归检查在修改前后都通过,并具有相同的预存在失败集——没有新的红色,也没有静默消失的红色(停止运行的测试属于行为变更)。
- 对于产生输出的表面(生成器、序列化器、格式化器、报告),对输出进行哈希处理:在修改前捕获对相同输入的黄金输出哈希,并在修改后逐字节比较。哈希不匹配是一个需要呈现和解释的行为差异,绝不能忽视;由调用者决定是保留、收窄还是撤销更改。
- 更改表面上可观察的错误消息、退出代码和公共签名属于行为,除非调用者将其排除。
事后被跳过或收窄的中立性门控是事后中立性失败模式——差异决定了测试的内容。在报告的“行为未检查”列表中列出门控未覆盖的所有表面。