小马的世界

读书笔记-软件设计的哲学【15】先写注释

2026-06-22 · 9 min read

将注释作为设计过程的一部分

许多开发者都会把编写文档推迟到开发流程的最后,也就是编码和单元测试全部完成之后。这几乎是产生低质量文档最可靠的方法之一。编写注释的最佳时机,其实是在开发过程的开始阶段,也就是写代码的同时。先写注释,会让文档编写成为设计过程的一部分。这样做不仅能够产生更好的文档,还能带来更优秀的设计,并且让文档编写过程变得更加愉快。

延迟编写的注释就是糟糕的注释

几乎我遇到过的每一个开发者都会推迟写注释。当你问他们为什么不早点写文档时,他们通常会说代码还在不断变化。如果现在就写文档,等代码修改后还得重写;因此不如等代码稳定下来再写。不过,我怀疑还有另一个原因:他们把写文档视为一种苦差事,因此总是尽可能往后拖延。

遗憾的是,这种做法会带来几个负面后果。首先,推迟写文档往往意味着文档最终根本不会被写出来。一旦开始拖延,就很容易再拖一会儿;毕竟,再过几周代码会更稳定一些。等到代码真正稳定下来时,代码量已经变得很大,补写文档的工作也随之变得庞大而令人抗拒。你永远找不到一个方便的时间,停下手头工作几天,把所有缺失的注释补齐。

而且,人们很容易说服自己:对于项目来说,更重要的是继续推进,去修复 Bug,或者开发下一个新功能。这样一来,只会留下更多没有文档说明的代码。

即使你真的有足够的自律,能够回过头来补写注释(别自欺欺人了,大多数人做不到),这些注释的质量通常也不会太高。到了那个阶段,你在心理上早已从这段代码中抽离出来。在你看来,这部分工作已经结束,你迫不及待地想投入下一个项目。你知道写注释是正确的事情,但一点也不有趣。你只想尽快完成它。于是,你会草草浏览代码,加上一些刚好能让代码看起来体面的注释。

而此时,距离你设计这段代码已经过去了一段时间,你对设计过程的记忆开始变得模糊。你会一边阅读代码一边写注释,因此注释往往只是代码内容的重复。即使你努力回忆那些无法从代码直接看出来的设计思想,也总会有一些内容被遗忘。因此,这些注释恰恰遗漏了它们最应该描述的重要信息。

先写注释

我采用一种不同的方式来写注释:在开发的最开始阶段就把注释写好。

  • 对于一个新类,我首先编写类的接口注释(class interface comment)。
  • 接下来,为最重要的公共方法编写接口注释和函数签名,但暂时不实现方法体。
  • 我会反复调整这些注释,直到整体结构看起来合理为止。
  • 然后,我为类中最重要的成员变量编写声明和注释。
  • 最后,再补充各个方法的实现,并在需要时添加实现层面的注释。
  • 在编写方法体的过程中,我通常会发现还需要增加新的方法和成员变量。对于新增的方法,我会先写接口注释,再写方法体;对于新增的成员变量,我会在声明变量的同时写好注释。

当代码完成时,注释也同时完成了。不会留下任何等待补写的注释积压。

先写注释的方法有三个好处。首先,它能够产生更好的注释。如果你在设计类的过程中同步编写注释,关键的设计问题会始终保持在你的脑海中,因此很容易把它们记录下来。对于每个方法,最好在编写方法体之前先写接口注释,这样你就能够专注于方法的抽象和接口,而不会被实现细节分散注意力。在编码和测试过程中,你会不断发现并修正注释中的问题。因此,注释会随着开发过程不断得到改进。

注释是一种设计工具

第二个,也是最重要的好处,是在开发开始阶段编写注释能够改善系统设计。注释是完整表达抽象(abstraction)的唯一方式,而优秀的抽象正是优秀系统设计的基础。如果你一开始就通过注释描述这些抽象,就能够在编写实现代码之前先审视并调整它们。

为了写出好的注释,你必须识别出一个变量或一段代码的本质:这个东西最重要的方面是什么?在设计过程的早期完成这项工作至关重要,否则你就只是在机械地堆砌代码。

注释就像复杂度矿井里的金丝雀。如果一个方法或变量需要很长的注释才能说明清楚,那就是一个危险信号,说明你的抽象设计并不好。回顾第 4 章中的观点:类应该是“深”的(deep);最好的类拥有非常简单的接口,却能实现强大的功能。

判断一个接口复杂度的最佳方法,就是查看描述它的注释。如果一个方法的接口注释既简洁又包含了使用该方法所需的全部信息,那么这说明该方法拥有简单的接口。反过来说,如果你无法不用一大段复杂注释就完整描述这个方法,那么这个方法的接口本身就很复杂。

你可以将方法的接口注释与其实现进行对比,从而判断它到底有多“深”。如果接口注释必须把实现中的主要特性全部描述出来,那么这个方法就是“浅”的。

同样的道理也适用于变量。如果一个变量需要很长的注释才能被完整解释,那也是一个危险信号,表明你可能没有选择合适的变量分解方式(variable decomposition)。

总体而言,编写注释这一行为本身,就能帮助你尽早评估设计决策,从而发现并修复问题。

当然,只有当注释完整且清晰时,它们才能成为衡量复杂度的有效指标。如果一个方法接口注释没有提供调用该方法所需的全部信息,或者写得过于晦涩难懂,那么这样的注释就无法准确反映该方法到底有多“深”。

🚩危险信号:难以描述(Red Flag: Hard to Describe)

描述一个方法或变量的注释应该既简单又完整。

如果你发现自己很难写出这样的注释,那么这往往意味着你正在描述的对象本身存在设计问题。

早期编写的注释才是有趣的注释

尽早编写注释的第三个也是最后一个好处,是它能让写注释变得更有乐趣。

对我来说,编程最令人享受的部分之一,就是为一个新类进行早期设计的阶段。在这个阶段,我会不断完善类的抽象和结构。我的大部分注释都是在这个阶段完成的,而这些注释也是我记录和检验设计质量的方式。

我追求的是一种能够用尽可能少的文字,就完整而清晰表达出来的设计。注释越简单,我对自己的设计就越满意。因此,发现那些简洁而有力的注释,本身就是一种成就感的来源。

如果你是在以战略性的方式编程,把目标放在创造优秀设计,而不仅仅是写出能运行的代码,那么编写注释本应是一件有趣的事情,因为它正是帮助你发现最佳设计的方法。

提前写注释的成本高吗?

现在让我们重新审视一下推迟写注释的理由:这样做可以避免随着代码演进而不断修改注释的成本。

一个简单的粗略估算就能说明,这种节省其实微乎其微。

首先,估算一下整个开发过程中你花在编写代码和注释上的总时间(包括后续修改代码和注释的时间)。这个比例很难超过整个开发时间的 10%。

即使你的代码中有一半行数都是注释,编写注释所花费的时间大概也不会超过整个开发时间的 5%。

即便把注释全部拖到最后再写,你节省下来的也只是这 5% 中的一小部分,并不算多。

先写注释意味着在开始写代码之前,抽象设计已经更加稳定。这很可能会在编码过程中节省时间。相反,如果先写代码,抽象设计往往会随着开发不断演变,从而导致比“先写注释”方法更多的代码修改。

综合考虑这些因素之后,整体来看,先写注释甚至有可能更快。

总结

如果你还从未尝试过先写注释,不妨试试看,并坚持一段时间直到习惯这种方式。

然后思考它对以下几个方面的影响:

  • 注释的质量;
  • 设计的质量;
  • 以及你对软件开发整体过程的享受程度。

当你实践了一段时间之后,欢迎告诉我你的体验是否与我的一致,以及为什么一致,或者为什么不一致。