变更卫生与代码工艺 是做什么的?
概述
变更如何组织、代码如何编写。两个主题:将结构性变更与行为性变更分离,以及用最简洁且有效的方式表达意图。
变更卫生
- 先做结构性变更,再做行为性变更,并分别独立验证。 重组(拆分文件、重命名、添加注释)是一个独立的、可单独检查的步骤,应与行为变更(新增约束、修改逻辑)分开。永远不要将它们混合在一个不可分割的变更中——否则评审者无法区分安全的重组与真正的逻辑更改。
- 每个变更只关注一件事。 一个变更应具有单一、可陈述的目的。
- 在你工作的代码中进行改进,当它的问题影响你的任务时(让营地比来时更整洁)——但不要启动无关的重构。始终聚焦于目标。
代码工艺
- 显式优于隐式。 不使用通配符代替名称,不进行隐式类型转换,不使用空字符串或魔法哨兵值。说出你的意图。
- 描述性命名。 函数/变量描述其功能;遵循语言和生态系统的惯用风格。
- 注释意图,而非机制。 在非显而易见的场景下解释为什么;对自解释的代码跳过注释。
- 小型的、单一职责的单元。 对于任何单元,你应该能够说出它的功能、如何使用、依赖什么——而无需阅读其内部实现。一个大文件通常意味着做得太多。
- 在生产代码中谨慎地遵循DRY原则。 重复是一种信号,而非自动错误。第二次出现近乎相同的代码时,触发一次有意识的决策:要么抽象,要么记录为什么不抽象。抽象共享的含义,而非偶然的形态——看起来相似但因不同原因而变化的代码应保持分离。
- 遵循生态系统标准。 使用一位称职从业者所预期的惯用方式和受良好支持的库。不要重新发明平台已解决的轮子。
命名的张力
- DRY vs. 过早抽象: 生产代码只在意图被证实的重复上进行抽象,而非预期中的形态重复。不确定时,等到第三次出现(YAGNI 决定最终判断)。
- DRY vs. DAMP: 生产代码应 DRY;测试代码应 DAMP(清晰优先于去重,见
tests-as-a-control)。将 DRY 应用于测试是一种违规。
常见合理化说辞
| 借口 | 现实 |
|---|---|
| “我会在同一个提交里重命名并添加功能” | 结构与行为混合在一起的一次 diff 是无法评审的。请将它们分开。 |
| “有两份拷贝,我立刻就要 DRY 化” | 第二次出现 = 做决策。相同形状 ≠ 相同含义。偶然的重复应保持分离。 |
| “既然我在这里,顺便重构另一个模块” | 无关的重构会扩大范围并增加风险。记下来;现在不要做。 |
| “聪明的单行更优雅” | 显式胜过巧妙。为下一位读者优化。 |
危险信号——停下来
- 一个 diff 既移动代码又改变其行为
- 一个 PR 的目的需要用“和”字来描述
- 在第一次见到相似时就进行抽象,或者基于预期(而非实际)的重复
- 一个你无法再完全记住的文件
完整原理:见章程第 V 条和第 VI 条,位于 engineering-constitution/references/engineering-constitution.md。