我在四个团队待过,每个团队都开过至少一次「我们要重视文档」的会。四次会议的结果一模一样:会后一个月内文档增加,三个月后回到原点。
这篇不谈「文档很重要」,这句话没人反对,说了也没用。我想做的是把我听过的所有借口列出来,然后逐条掰扯——包括我自己用过的那些。
借口一:代码即文档
说这话的人通常写代码确实不错。但这句话偷换了概念。
代码能告诉你是什么,永远告诉不了你为什么。我可以从代码里看出这里重试了 3 次,但我看不出为什么是 3 次不是 5 次。而这个「为什么」——比如「上游 SLA 是 99.9%,三次重试后失败概率低于我们的容忍阈值」——才是真正会丢失的知识。
我在一个项目里见过 Thread.sleep(2100)。2100 毫秒。没有注释。写这行的人已经离职三年。没人敢改,因为不知道那个奇怪的数字来自哪里。这段代码活得比它的解释久太多了。
借口二:文档会过时,过时的文档比没有更糟
这是最有迷惑性的一条,因为前半句是真的。
但结论错了。按这个逻辑,代码也会有 bug,所以不如别写代码。
真正的问题是我们写了会过时的文档。什么样的文档会过时?描述实现细节的。什么样的不会?描述决策和约束的。
我现在只写三类文档,它们的过时速度差了一个数量级:
- 决策记录:某年某月,我们在 A 和 B 之间选了 A,因为这三个原因,当时的约束是这些。这类文档永远不会过时,因为它记录的是一个历史事实。就算后来改成了 B,这份记录依然有价值。
- 怪癖清单:这个系统里所有反直觉的地方。比如「上游的时间戳不带时区,实际是东八区」。这类东西改动频率极低。
- 入口地图:一个新人想改某类功能,从哪个文件开始看。这个会过时,但一年更新一次就够了。
我不写的是:API 参数说明(让代码生成)、数据库字段解释(写在建表语句的 comment 里)、部署步骤(写成脚本)。
借口三:没时间
这条我自己用得最多,所以我格外了解它的虚伪。
去年我做过一个统计。我们组有个模块,半年里被不同的人问了 23 次同样类型的问题,我粗略估计每次沟通双方各花 15 分钟,加上上下文切换,总成本大概 20 小时。
而写一份能回答这些问题的文档,我实际花了 2 小时 40 分钟。
更要命的是,那 23 次里有 6 次是我自己问自己——我看着三个月前写的代码,想不起来当时为什么那么做。
借口四:我写了没人看
这条通常是真的,但原因往往不是「没人愿意看」,而是「找不到」或者「不敢信」。
我在一个项目里见过文档散落在四个地方:仓库里的 md 文件、内部 wiki、一个共享文档、还有某人的个人笔记。没有人知道哪份是最新的,于是所有人都选择直接问人。
我后来的做法非常粗暴:文档只放在代码仓库里,跟代码一起走 review 和版本。放在别处的一律视为不存在。这样至少「哪份是最新的」这个问题有唯一答案。
我自己的实际做法
说了这么多,我并不是一个文档写得很好的人。我的做法很功利:
- 只在痛点处写。被问到第二次的问题,我就写下来。第一次不写,因为可能只是偶然。
- 写在最靠近代码的地方。能写成注释的不写成文档,能写成文档的不写成 wiki。距离越近,一起被改的概率越大。
- 决策记录用固定模板,五段:背景、选项、决定、理由、当时的约束。写一份大概 20 分钟。我们组两年积累了 40 多份,新人入职我直接让他按时间顺序读一遍,比任何架构图都管用。
- 接受不完整。我以前写文档总想写全,结果一篇都写不完。现在我允许自己写半截,标个 TODO 就发出去。
一个反常识的结论
我发现文档写得最好的时刻,不是项目开始,也不是项目结束,而是我要休假之前。
因为那时候有一个真实的、迫在眉睫的交接需求。人只在有具体接收者的时候才写得出好文档。
所以我现在写文档会给自己设一个想象中的读者:三个月后的我,已经忘光了所有上下文,正在被一个线上问题追着跑,只有五分钟时间。为那个人写,写出来的东西通常都能用。