代码是写给人看的 — 读代码的人才是最贵的资源
这一章讲四件事: 为什么代码的第一读者是人不是机器(有账); 三笔具体的成本账——命名、手动执行、代码审查;写注释的正确时机(比你想的早); 以及哪些编码原则作者 2021 年已经撤销。全章一句话:让下一个读它的人少花时间,就是让最贵的资源少浪费。
1. 先看现象:一年后的你,读不懂一年前的你
每个维护过别人代码的人都有同款经历:一段逻辑明明能跑,读半小时才敢动 一个字符;
一个变量名 N_FLT,全文件上下翻找它是什么的缩写;一个函数返回值正常,
背后却悄悄改了三个全局的东西。
计算机时代早期这样写有理由:机器时间贵,人的时间便宜,能省一条指令是一条。 作者引用的时代转折是:现在最有价值的资源是人力——开发、维护、增强软件的人力; 除了极少数例外,程序员首先考虑的应该是后续要理解和改造这段软件的人1。 机器越来越便宜,人越来越贵——代码的第一读者,从 CPU 换成了人。
「执行效率仍然重要,但和可读性不互斥」——需要快可以快,但别以牺牲可读性为代价1。
2. 顶层全景:三笔成本账
写代码的时刻 未来读/改代码的时刻
│ │
命名:多敲 3 键 ──────────────► 少写 1 行注释,读者不用猜
│ │
写完手动跑 30 分钟 ──────────► 省下 3~4 人天的系统级排障
│ │
审查花 15% 研发资源 ─────────► 抓住 82% 的错误,省 50%~90% 测试时间
图说:本章所有原则共用同一个形状——现在多花一点点,
换未来少花一大截。账都在书里,下面一笔笔算。
3. 核心原理
3.1 主走查:一次命名,到底贵几块钱
原则 91 的例子是全书少数能精确到「按键次数」的成本账,值得原样走完2:
优秀的程序员只把 10%~15% 的时间花在敲键盘上,其余都在思考——所以「短名字省敲击」这个理由 本身就站不住(省的是那 10% 里的一小截)。但作者给了更硬的一笔账:
写法 A(短名+补注释):
N_FLT = N_FLT + 1
还要配一行注释「LOOK AT NEXT FLIGHT」
→ 代码 17 键 + 注释 32 键 = 49 键
写法 B(好名,免注释):
NEXT_FLIGHT = PREVIOUS_FLIGHT + 1
→ 29 键,不需要注释
图说:数字来自原书。「看下一个航班」这层意思,
A 写进注释,和代码隔一层;B 直接写在名字里,和代码同层。
B 比 A 还少敲——而真正的收益不在敲键,在半年后读这段代码的人:
A 的读者要先看注释才知道 N_FLT 是航班序号,还要祈祷注释没过期;
B 的读者看名字就够了。注释会过期,名字不会——它和它描述的东西长在一起。
同族的原则一排:
- 别耍技巧(原则 87):用晦涩手法写「聪明」代码,典型是靠函数的副作用(操作的主要目的之外、 顺带对外产生的效果)干活。书里给了三个动机——炫耀聪明、给解读者设置智力关卡、职业安全感—— 并引 Macro 的判词:那「通常只是愚蠢地使用了高智商」3;
- 别用全局变量(原则 88):全局变量=任何代码都能直接改的公共数据。读到「船的数量 = -16.3 艘」时, 你无法定位是谁改坏的——「全局」意味着嫌疑人是全部人。替代:把数据封装进模块,或显式传参; 参数(递给模块的输入值)多得离谱,说明该重新设计的是结构4;
- 别埋副作用(原则 90):副作用是「许多细微错误的来源」——潜伏最深、症状最难追的那类5;
- 格式统一(原则 105):用哪种缩进风格无关紧要,保持一致才要紧;唯一比不一致更糟的是 「错误的缩进」——把 ELSE 对到了错误的 IF 底下,格式在撒谎6。
3.2 第一笔大账:写完先手动跑一遍——30 分钟对 3~4 人天
原则 97 的建议朴素到可笑:每个组件写完,手动执行几个简单用例,大约花 30 分钟。 作者算的账7:
做:6 个嫌疑组件 × 30 分钟手动执行 ≈ 3 小时
不做:系统测试失败 → 3~4 人天定位 → 6 个组件逐个深查 → 每个 30 分钟手动补跑
图说:原书给出的算式。「现在省 30 分钟」的复利是「将来赔人天」。
注意限定:手动执行是补充,不替代程序化的单元测试(对独立组件的自动化验证,第 08 章细讲)7。 它买到的是另一种东西——写代码的人当场「看见」自己的代码怎么走,而不是三个月后从 日志(程序运行时自动记下的流水账)的灰烬里考古。
3.3 第二笔大账:代码审查——15% 的资源,买 82% 的错误
原则 98 引的是全书最硬的一组数8:
- Fagan 1976 年提出代码审查(一群人开会逐行读代码找错,正式流程,行话也叫 inspection/review);
- 审查(连同详细设计评审)能发现全部错误的 82%——「对发现错误而言,代码审查比测试好得多」;
- 成本:约 15% 的研发资源;回报:总开发成本净减 25%~30%;
- 顺带省下 50%~90% 的测试时间——错误在审查桌上被抓,就不用在测试环境里排队等死。
为什么「人读」比「机器跑」抓得多?第 08 章会给出机制层面的答案(测试只能证明缺陷存在,且只在你想到的输入上跑; 人读代码时,眼睛会扫过所有输入)。这里先记结论:审查不是仪式,是本章所有单项原则的一次成套执行—— 命名、技巧、副作用、嵌套,一个下午的审查全查一遍。
3.4 注释的正确时机:写代码之前
两条原则连发。原则 95:别等代码定稿才写注释——调试时你会遇到两类错: 转换过程的错(只改代码)和算法的错(注释和代码都要改);不写注释,算法错根本无从对照发现9。
原则 96 更进一步:先写文档、再写代码。流程是:完成详细设计 → 把外部接口和算法写成注释 → 让注释通过编译 → 把每条注释翻译成对应的代码。书里还附了自检:如果最后发现每条注释只对应一行代码, 说明算法描述得过细了10。
这条听起来像苦行,但它的机制清楚:注释先行=把「思考」和「打字」分开排队; 翻译注释的过程本身就是第一次逐行走查——等于把 3.2 的 30 分钟手动执行,提前到了键盘上。
作者 2021 年自白:96 仍是他最爱的原则之一,他一直实践,同事认为他疯了11。
3.5 语言的三条:选对、别甩锅、别迷信语言知识
三条合讲,因为它们管同一个决定:「用什么语言写」12:
| 原则 | 一句话 | 例子(书里的) |
|---|---|---|
| 用合适的语言(102) | 按首要目标选:可移植选 C/FORTRAN;快速开发选 4GL/BASIC;低维护选 Ada/Eiffel;客户指定 Y 就用 Y | 目标决定语言,不是流行决定语言 |
| 语言不是借口(103) | 被迫用不理想的语言,照样能写出高质量程序 | 「我们只会 C」不是选型依据 |
| 语言知识没那么重要(104) | 优秀的程序员用什么语言都优秀;「优秀的 C 程序员」同时「糟糕的 Ada 程序员」这种组合基本不存在——真优秀的人学新语言很快 | 选型依据是合适,不是团队会的语言 |
3.6 结构、嵌套与时机
- 嵌套不超三层(原则 101):
IF里套IF再套IF,超过三层可理解性严重下降—— 人脑记住逻辑的能力有限,这是 3±2 规则(第 06 章 3.3)在代码里的样子13; - 结构化≠好(原则 100):只用规范控制结构是高质量程序的必要条件,远非充分—— 精心组织过的垃圾照样是垃圾14;
- 别太早编码(原则 106):地基(需求、设计)没浇好就开工,后改的代价想盖房子就知道。 但书里同样记了 Lehman 的反向警告:也别太晚编码——拖着不落地,设计里的问题永远不会暴露15;
- 手册要薄(原则 18,顺带回望):衡量软件质量的办法之一是看用户手册的厚度—— 设计良好的软件,用法应该不言而喻;把手册搬到网上(在线帮助)不算变薄16。
4. 作者的判断与证据
| 说法 | 谁的 | 证据 |
|---|---|---|
| 人力取代机器时间成为最贵资源 | 作者(原则 92)1 | 时代观察(算力价格持续下降) |
| 32 键 vs 29 键 | 书内原始算例2 | 书内可复核的按键计数 |
| 手动执行 30 分钟 vs 3~4 人天 | 作者的经验算式7 | 经验比例,非统计 |
| 审查发现 82% 错误、净省 25%~30% | 作者引 Fagan/Grady8 | 有文献支撑的实测数据(Fagan 1976 起) |
| 「先正确再提速」不再需要 | 作者 2021 改口17 | 现代编译器/硬件前提;1995 年立场的撤销 |
| 注释先行仍是最爱 | 作者 2021 自述11 | 个人实践自报 |
判断(我们的,不是书里的): 3.1~3.3 三笔账在今天有两个共同的放大器——代码评审工具和 代码搜索让「被读次数」远高于 1995 年(一段代码一年可能被读几百次)。于是同一笔命名账的乘数变大: 当年省 1 行注释,今天等比放大成省几百次困惑。可读性投资的回报率,三十年间是涨的,不是跌的。 如果错,会错在: 如果 AI 辅助的「代码即草稿、读 的也是 AI」成为主流,人类阅读频次下降会把这个乘数打回去—— 但评审与事故调查仍然落在人身上,所以该折掉的只是「复读次数」那部分,不是全部。
5. 边界与局限
- 「先正确再提速」已被作者撤回17——但注意他撤的是「别过早担心微优化(小处的提速改动)」这个焦虑, 不是第 06 章「选对算法」那条:差一个数量级的算法,再快的硬件也追不平 (数量级:十倍一百倍那种跨档的差距)。
- P99~100(「允许任意乱跳转」的语言、GOTO 改造)已成历史:今天主流语言都强制规范写法; 这两条的余值是「结构只是必要条件」14——现代等价物是:用了规范框架≠代码好。
- 手动执行与审查的成本账是 1995 年的人工时价。今天的自动格式化、静态检查、AI 辅助读码 承包了其中一部分(作者 2021 也提到原则 124 的测量已自动化18); 但「人逐行读一遍」发现的错(错误但能跑的逻辑)机器检查仍然抓不全。
- 「语言知识没那么重要」有度。它驳的是「只会 C 所以只能选 C」; 不成立的方向是「所以随便 让新手用最难的语言」——书里同时保留了「按目标选语言」12。
6. 可带走的
- 代码的第一读者是人:人力是最贵的资源,可读性是给最贵资源省时间;
- 好命名是「自带注释」:32 键加注释输给 29 键免注释;注释会过期,名字不会;
- 写完每个组件,花 30 分钟手动跑几个用例——省下的是 3~4 人天的系统级排障;
- 代码审查值得供起来:15% 资源 → 82% 的错误、25%~30% 净成本、50%~90% 测试时间;
- 注释在写代码之前写,并让每条注释对应一段代码——翻译注释就是第一次走查;
- 副作用是潜伏最深的错误来源;全局变量让「谁改坏的」变成无头案;
- 嵌套不过三层;用了规范结构不等于写得好;
- 语言按目标选,不由团队会什么、也不由流行什么决定;被迫用不理想的语言不构成质量借口;
- 用户手册越薄,软件越好——在线帮助也算手册页数。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 程序首先写给人看 | 第5章 编码原则 | text/17-ch05.txt:91(搜「最有价值的资源」) |
| 别耍技巧(高智商) | 第5章 编码原则 | text/17-ch05.txt:19(搜「愚蠢地使用了高智商」) |
| 全局变量(-16.3 艘船) | 第5章 编码原则 | text/17-ch05.txt:37(搜「16.3」) |
| 副作用 | 第5章 编码原则 | text/17-ch05.txt:59(搜「副作用」) |
| 命名的按键账 | 第5章 编码原则 | text/17-ch05.txt:77(搜「NEXT_FLIGHT」) · text/17-ch05.txt:71(搜「10%~15%」) |
| 最优数据结构 | 第5章 编码原则 | text/17-ch05.txt:101(搜「两个或三个」) |
| 手动执行(30 分钟/人天) | 第5章 编码原则 | text/17-ch05.txt:139(搜「30分钟」) |
| 代码审查(82%/15%) | 第5章 编码原则 | text/17-ch05.txt:147(搜「82%」) · text/17-ch05.txt:149(搜「50%至90%」) |
| 注释先于定稿 | 第5章 编码原则 | text/17-ch05.txt:71(搜「注释」) |
| 文档先于编码(过细自检) | 第5章 编码原则 | text/17-ch05.txt:131(搜「过于细致」) |
| 语言三条(选型/借口/知识) | 第5章 编码原则 | text/17-ch05.txt:191(搜「可移植性」) · text/17-ch05.txt:199(搜「借口」) · text/17-ch05.txt:211(搜「Ada程序员」) |
| 嵌套三层 | 第5章 编码原则 | text/17-ch05.txt:181(搜「三层」) |
| 结构化必要非充分 | 第5章 编码原则 | text/17-ch05.txt:173(搜「充分条件」) |
| 别太早编码(地基/别太晚) | 第5章 编码原则 | text/17-ch05.txt:237(搜「地基」) · text/17-ch05.txt:239(搜「不要太晚」) |
| 格式化(错误缩进) | 第5章 编码原则 | text/17-ch05.txt:225(搜「不正确的缩进」) |
| 手册要薄 | 第2章 一般原则 | text/14-ch02.txt:169(搜「用户手册」) |
| 2021:96 仍最爱 | 作者序 | text/08-fm.txt:53(搜「疯」) |
| 2021:94 撤回 | 作者序 | text/08-fm.txt:51(搜「94」) |