人工审查
对于每个更改,问:它服务于什么需求,它改变了什么行为,以及是否有现有机制可以完成该工作?
仅审查的请求不授权进行编辑。请遵循项目的约定和用户的范围,包括测试和发布限制。
追踪行为
比较新旧执行路径,包括调用者和绕过分支。区分传递对象、传递工厂和调用它。确定谁拥有状态、何时加载状态以及哪些操作会看到替换。
在移除重复代码之前先调查其存在的原因。是哪次更早的更改使得这个操作成为必需?新的实现可能丢失了隐式的启动检查或清理。在声称相同工作发生两次之前,先验证分支。
检查失败时机、重试、资源清理以及原始原因是否到达用户。从启动阶段移动到连接设置阶段的错误可能会改变行为,即使两个路径最终都会失败。在将内部异常类型视为兼容性要求之前,先检查其消费者。
简化责任层
首先检查接收组件已经支持什么。如果它的契约足够,则修正调用者提供的内容,而不是为接收者增加另一个责任。不要仅仅为了让调用者看起来相似而将工作移入共享构造函数。
通过当前任务来证明每个新增的操作、字段、分支或抽象是合理的。对于删除操作,检查依赖的调用者和保证。优先选择现有的兼容契约;保留那些执行有意义转换或强制契约的适配器。
在整个受影响的路径上跟踪状态和工作。如果调用者在闭包中捕获了相同的值,移除缓存并不能修复过时状态。相反,动态提供者和基于文件的回退可能故意在不同的时间加载。将它们统一可能引入回归。
根据概念、状态所有者和读者必须追踪的间接层来判断简洁性。更小的差异并不总是更清晰的设计。避免不相关的格式化和为未来任务做准备。
测试需求
每个测试应通过其名称、输入、操作和预期结果讲述一个连贯的案例。仅保留建立该案例所需的设置和断言。当保证需要时(例如故障后的恢复),几个阶段可以放在一起;不相关的案例则不行。
保留能够区分所需行为和回归的最小集合。内部调用计数仅在它们表达需求时才有意义,例如只加载一次。确认注入的故障确实发生了:超时或关闭的套接字本身可能因错误原因而通过。
按照相关行为对测试进行排序,以便读者将有用的上下文带入下一个测试。使用具体的名称而不是解释性注释。将注释视为首先改进代码的提示;保留那些无法在名称或结构中清晰表达的原因。
在添加自定义夹具、等待、断言或资源辅助工具之前,先查找现有的框架或基类设施。优先选择直接的代码。当保持可理解性时,共享重复的设置,但保持场景特定的输入和操作可见。避免使用通过隐藏案例来节省行数的回调、标志或通用辅助工具。
在断言需要的事件上进行同步,而不是依赖延迟或另一个线程中的更早事件。在失败时保留清理操作。在没有差异的情况下按顺序阅读最终的测试,并检查它们的目的在没有讨论的情况下是否清晰。
解释并交付
保留解释非显而易见原因并防止可能错误的简化的注释。移除对明显操作的叙述。保持 API 契约精确性和用户指令的可操作性;在没有差异的情况下阅读编辑过的部分,以检查它们是否能够独立存在。
将用户的诊断和您的第一印象都视为假设。报告有支持的发现,包括触发场景、后果、证据以及最小的有用修正。将缺陷与未决问题和可选的改进分开。没有发现也是一种有效的结果。
对于重大的重新设计,解释一个具体的执行流程,什么变得更简单,以及行为或兼容性成本。与用户解决同意范围之外的选择;继续常规授权的修复而无需再次批准。
修复后,阅读完整的受影响流程和最终差异。报告结论、执行的检查以及剩余的差距,而不是对每一行提出疑问。