跳到主要内容

代码是写给人看的 — 读代码的人才是最贵的资源

这一章讲四件事: 为什么代码的第一读者是人不是机器(有账); 三笔具体的成本账——命名、手动执行、代码审查;写注释的正确时机(比你想的早); 以及哪些编码原则作者 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. 可带走的

  1. 代码的第一读者是人:人力是最贵的资源,可读性是给最贵资源省时间;
  2. 好命名是「自带注释」:32 键加注释输给 29 键免注释;注释会过期,名字不会;
  3. 写完每个组件,花 30 分钟手动跑几个用例——省下的是 3~4 人天的系统级排障;
  4. 代码审查值得供起来:15% 资源 → 82% 的错误、25%~30% 净成本、50%~90% 测试时间;
  5. 注释在写代码之前写,并让每条注释对应一段代码——翻译注释就是第一次走查;
  6. 副作用是潜伏最深的错误来源;全局变量让「谁改坏的」变成无头案;
  7. 嵌套不过三层;用了规范结构不等于写得好;
  8. 语言按目标选,不由团队会什么、也不由流行什么决定;被迫用不理想的语言不构成质量借口;
  9. 用户手册越薄,软件越好——在线帮助也算手册页数。

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」)

Footnotes

  1. 出处:「第5章 编码原则」第 91 段(text/17-ch05.txt:91,搜「最有价值的资源」)。「执行效率也很重要……并不是互斥的」同段。 2 3

  2. 出处:「第5章 编码原则」第 77 段(text/17-ch05.txt:77,搜「NEXT_FLIGHT」)。原文算例:N_FLT = N_FLT + 1 需注释「LOOK AT NEXT FLIGHT」(按键 32 次),NEXT_FLIGHT = PREVIOUS_FLIGHT + 1 不需注释(按键 29 次);10%~15% 的敲码时间占比(搜「10%~15%」)同段。汇总成 49 键的加法是我们做的。 2

  3. 出处:「第5章 编码原则」第 19 段(text/17-ch05.txt:19,搜「愚蠢地使用了高智商」)。三个动机与副作用的典型表现同段。

  4. 出处:「第5章 编码原则」第 37 段(text/17-ch05.txt:37,搜「16.3」)。替代方案(封装/传参/参数过多=重新设计)同段。

  5. 出处:「第5章 编码原则」第 59 段(text/17-ch05.txt:59,搜「副作用」)。

  6. 出处:「第5章 编码原则」第 225 段(text/17-ch05.txt:225,搜「不正确的缩进」)。

  7. 出处:「第5章 编码原则」第 139 段(text/17-ch05.txt:139,搜「30分钟」)。6 组件×30 分钟、3~4 人天、「补充而不是代替」同段。 2 3

  8. 出处:「第5章 编码原则」第 147 段(text/17-ch05.txt:147,搜「82%」)。Fagan 1976、15% 研发资源、净减 25%~30%(搜「25%~30%」)、「减少50%至90%的测试时间」(搜「50%至90%」)同段。 2

  9. 出处:「第5章 编码原则」第 71 段(text/17-ch05.txt:71,搜「注释」)。

  10. 出处:「第5章 编码原则」第 131 段(text/17-ch05.txt:131,搜「过于细致」)。流程(注释先过编译、再逐条翻译成代码)同段。

  11. 出处:「作者序」第 53 段(text/08-fm.txt:53,搜「疯」)。 2

  12. 出处:「第5章 编码原则」第 191 段(text/17-ch05.txt:191,搜「可移植性」)、第 199 段(text/17-ch05.txt:199,搜「借口」)、第 211 段(text/17-ch05.txt:211,搜「Ada程序员」)。语言清单的时效性:中文版译者注已注明 FORTRAN、COBOL、SNOBOL 等多已不常用,思想照旧。 2

  13. 出处:「第5章 编码原则」第 181 段(text/17-ch05.txt:181,搜「三层」)。

  14. 出处:「第5章 编码原则」第 173 段(text/17-ch05.txt:173,搜「充分条件」)。 2

  15. 出处:「第5章 编码原则」第 237 段(text/17-ch05.txt:237,搜「地基」)。Lehman 的反向观点「不要太晚编码」(搜「不要太晚」)同段。

  16. 出处:「第2章 一般原则」第 169 段(text/14-ch02.txt:169,搜「用户手册」)。在线帮助也算手册、开发者喜欢的界面≠用户会用、用户要简单干净(搜「内置技巧」)同段。

  17. 出处:「作者序」第 51 段(text/08-fm.txt:51,搜「94」)。 2

  18. 出处:「作者序」第 59 段(text/08-fm.txt:59,搜「124」)。