自解释代码 vs. 注释:维护大规模代码库的经验教训
Source: Dev.to
过时注释的危险
注释的主要风险是 过时。当代码被重构时,开发者常常忘记同步更新相应的注释。随着时间推移,这些注释会“腐烂”,变得误导且危险。自解释代码避免了这个问题,因为逻辑本身就是唯一的真相——如果逻辑改变,作为“文档”的代码也必须随之改变。
自解释代码的支柱
1. 避免通用缩写
名称应当传达 是什么 与 如何做,无需额外说明。
// Bad
const d = 86400; // seconds in a day
// Good
const SECONDS_IN_A_DAY = 86400;
2. 封装条件判断
在 if 语句内部写复杂逻辑会增加认知负担。将逻辑提取到命名明确的变量或函数中。
// Bad
if (user.age > 18 && user.hasSubscription && !user.isBanned) {
// …
}
// Good
const canAccessPremiumContent =
user.isAdult && user.hasActivePlan && !user.isAccountFlagged;
if (canAccessPremiumContent) {
// …
}
3. 使用强类型作为活文档
枚举和接口取代“魔法字符串”,并使合法选项显式化。
// Dart example
enum OrderStatus { pending, shipped, delivered }
// Instead of passing a raw String for status, use OrderStatus.
何时使用注释
当代码无法解释 上下文 或 为何 做出某决定时,注释是有价值的。典型场景包括:
- 变通方案 – 例如
// Using a legacy loop here because the .map() polyfill fails in IE11. - 业务逻辑原因 – 例如
// We trigger this sync twice to ensure the third‑party API acknowledges the handshake. - 关键警告 – 例如
// CRITICAL: Do not change the order of these calls; it will cause a deadlock in the database.
对比:优缺点
| 方法 | 优点 | 缺点 |
|---|---|---|
| 自解释代码 | • 与逻辑始终保持同步 • 减少视觉噪音 • 强迫更好的命名 | • 不能解释外部的“为何” • 可能导致变量名过长 |
| 注释 | • 解释不明显的业务规则 • 提供 IDE 提示(如 JSDoc/DartDoc) | • 维护成本高 • 易“腐烂” • 常常掩盖“代码异味” |
文档层级
-
第 1 级 – 逻辑
编写干净、模块化的代码。如果需要注释来解释“步骤 1、步骤 2”,说明函数可能太大——应当拆分。 -
第 2 级 – 类型
使用 TypeScript、Dart 或类似的类型系统来定义数据结构。 -
第 3 级 – 文档字符串
为公共 API 添加 JSDoc/DartDoc,以在 IDE 提示中展示信息。 -
第 4 级 – 为什么的注释
仅在阅读完整洁代码后,开发者仍会问“我们为什么要这么做?”时才添加注释。
结论
高级工程师的目标不是写没有注释,而是写没有冗余的注释。代码库中的每一行文字都应当有价值。当代码足够清晰时,注释可以专注于高层策略,而不是低层语法,从而使整个系统更易于理解、维护和演进。