2.7

查看英文版

2.7 文档

概述与动机

文档(Documentation)是让人们能够使用、运维和修改软件的书面知识,而无需仅凭代码自己拼凑出理解。它有许多种体裁:如何入门、如何完成一项任务、系统是如何组织的、如何应对一次事件、一个 API 接受什么、返回什么。每一种都服务于有着不同需求的不同读者。好的文档不是可有可无的。它是能够在一个大型组织中扩展的知识,与只存在于少数几个人脑子里的知识之间的差别。

对于大型团队而言,文档是你抵御关键人物风险(key-person risk,即关键知识只掌握在一个或少数几个人手中的危险)的最佳防线,也是让新人快速上手的最快途径。当数百名工程师依赖着他们自己并未构建的系统,而人员又在不断加入、流动和离开时,只有当知识被写下来、容易被找到,组织才能正常运转。没有文档的系统会变得脆弱:只有它们的作者才能安全地修改它们,而一旦这些作者离开,组织就会失去维护自己软件的能力。这是规模化场景下最常见、也最昂贵的失败之一。

企业和政府的背景进一步提高了风险。系统存续的时间很长,所以你的文档必须能在最初的团队离开之后,服务于维护者数年、甚至数十年。监管和审计制度往往强制要求特定的文档作为控制证据:架构记录、操作手册(runbook,即逐步的运维和事件响应流程),以及决策日志。在供应商之间交接的公共部门系统,完全依赖文档来把知识带过合同的边界。然而文档又是出了名地容易腐坏过时,所以真正的挑战在于,随着软件的变化让文档保持准确。

关键原则

  • 为具体读者的具体需求而写;不同类型的文档服务于不同的目的。
  • 让文档紧靠代码存放,把它当作代码来对待(docs-as-code)。
  • 准确胜过完整;少量可信赖的文档,胜过大量错误的文档。
  • 能生成的就生成;不要手工维护一个工具能从真实来源自动生成的东西。
  • 主动对抗文档腐坏;过时的文档比没有文档更糟,因为它会误导读者。
  • 让文档可被发现;找不到的知识,等同于不存在。
  • 记录决策及其理由,而不仅仅是当前状态。

建议

采用代码化文档(docs-as-code)

把文档与它所描述的代码一起放在版本控制之中,用纯文本标记语言书写,并通过同样的拉取请求(pull request)流程进行评审。这样文档就能保持版本化、可评审,并且紧靠代码,让你能够把两者一起更新。通过一条自动化流水线发布文档,让最新版本始终可用。把文档当作代码来对待,能带来那种让代码保持可信的同一种纪律:评审、历史记录和自动化。

用 Diátaxis 框架来组织内容

把文档组织成四种截然不同的类型,因为混杂在一起对任何读者都没有好处:教程(tutorials,以学习为导向,面向新人)、操作指南(how-to guides,以任务为导向,针对某个具体目标)、参考(reference,以信息为导向,精确而完整),以及说明(explanation,以理解为导向,讲清楚原因和背景)。把这些分开,一切都会变得更容易撰写、导航和维护,因为每个页面都有一个清晰的任务和一个清晰的受众。

维护基本的运维文档

给每个仓库一个清晰的 README 作为它的前门:它是什么、如何构建和运行它,以及接下来该去哪里查看。为运维任务和事件响应编写操作手册,这样任何待命人员都能采取行动,而不仅仅是专家。维护解释系统结构和关键组件的架构文档。并提供能让新工程师迅速上手的入职文档。这些正是缺失时最让人怀念的文档。

从真实来源生成 API 文档和变更日志

从机器可读的契约或代码注解中生成你的 API 参考文档,这样它就无法偏离真实的接口。维护一份变更日志(changelog),最好是从结构化的提交记录或发布说明中生成,这样使用者就能看到版本之间发生了什么变化。把这些自动化,能让最容易腐坏过时的手工维护文档从你的工作清单上移除,并保持其可信度。

记录架构决策

把重大的架构和设计决策,以轻量级的、带有日期的记录形式捕获下来,说明背景、决策及其后果。这些决策记录保存了原本会失落的理由,让未来的维护者能够看到系统为什么是现在这个样子,而不是去质疑它或重蹈过去的覆辙。它们在企业和政府系统那种长期存续的生命周期中尤其有回报。

有意识地对抗文档腐坏

把过时的文档当作一个缺陷来对待。作为改变行为的同一次变更的一部分来更新文档,并把这当作一项评审要求。指定负责人,让每一份重要文档都有一个负责它的人。不时地复审高价值文档的准确性,剔除过时的内容,对任何你不再信任的内容加以移除或明确标注。腐坏最慢的文档,是”活的”文档:由系统生成,或针对系统本身进行测试。

投资知识管理和可发现性

通过良好的搜索、清晰的导航和一个明确的归属地,让文档变得可查找,这样人们就能找到自己需要的东西,而不必去问别人。不要让它碎片化地散布在太多互不相连的维基和工具之中。并且,在隐性知识()那些存在于聊天记录和人们脑子里的非正式理解()溜走之前,把它捕获成持久、可查找的形式。

权衡:优点与缺点

方法优点缺点
代码化文档(docs-as-code)版本化、可评审、紧靠代码;腐坏率低需要工程师的纪律;对非技术作者不够友好
维基 / 知识库易于编辑;人人可访问会偏离代码;容易碎片化;悄悄腐坏
生成式文档(API、变更日志)始终准确;维护成本低受限于来源所能表达的内容;需要工具支持
手写说明提供机器无法产出的丰富背景和理由耗费人力;容易过时
Diátaxis 结构每个页面目的清晰;更易导航和维护需要前期的结构化投入;要求作者的自律

核心权衡在于投入与准确性、持久性之间。最便宜的文档写法()一张快速搭起来的维基页面()也是最容易腐坏和碎片化的。最持久的文档()从源头生成、或像代码一样接受评审的文档()需要更多前期的纪律,但能保持可信。一条不错的经验法则:能生成的就生成,其余的紧靠代码存放,像代码一样接受评审,把耗费人力的手写说明留给那些只有人类才能提供的理由。

与团队讨论的问题

  1. 你们的文档是按读者需求分开的,还是教程、参考和说明混杂在同一个页面上? 本章推荐按 Diátaxis 把文档拆分为教程、操作指南、参考和说明,并把混杂各种类型列为一种对任何读者都没有好处的反模式。在规模化场景下,一个正在学习系统的新人,和一个正在寻找精确事实的待命工程师,需要不同的页面,而一个混杂的单一页面会拖慢这两者。带上这个信号:挑出你们访问量最高的文档,检查每一份是否都有一个清晰的任务和一个清晰的受众。把问题最严重的那些重新组织成不同的类型,这样每个页面都会更容易撰写、导航,并保持最新。正是这种结构,让文档在组织成长的同时依然可维护。

  2. 你们是否连同理由一起记录重大的架构决策,还是只记录当前状态? 本章推荐轻量级的、带有日期的决策记录,说明背景、决策和后果,并指出它们在企业和政府系统那种长期存续的生命周期中回报最大。没有它们,数年后的维护者就无法看出系统为什么是现在这个样子,于是他们要么质疑本来合理的选择,要么重蹈过去的覆辙。带上一个最近的艰难决策作为具体信号,它的推理过程现在可能只存在于一条聊天记录或某个人的记忆里。采用一种简短的决策记录格式,并把撰写它作为任何重大设计变更的一部分。这种理由正是只有人类才能提供、又在未被记录时腐坏得最快的那种知识。

  3. 仅凭你们的操作手册,任何一名待命工程师能否响应一次事件,而无需去呼叫构建这个系统的人? 本章把操作手册列为一项基本的运维文档,让任何待命人员都能采取行动,而不仅仅是专家,并描述了一个政府团队之所以能够接手一个系统,仅仅是因为操作手册把知识带过了合同的边界。关键人物风险正是它所防范的失败:当唯一的专家联系不上或已经离开时,一个没有文档记录的恢复流程会把一次常规事件变成一次停机。带上证据:拿一次最近的事件,检查仅凭操作手册是否就能解决它。为那些让人望而生畏的流程编写并测试操作手册,把一份无法独立支撑的操作手册当作一个缺陷来对待。这就是凌晨两点顺利恢复,与凌晨两点层层升级之间的区别。

  4. 你们的哪些 API 参考文档和变更日志是从真实来源生成的,哪些仍然是手工维护、正在悄悄偏离的? 本章要求你从机器可读的契约或代码注解中生成参考文档,这样它就无法偏离真实的接口,并把手工维护本可生成的内容列为一种反模式。对于一个大型团队而言,一份落后于真实接口的手写 API 文档,比没有文档还要糟糕:每一个信任它的使用者都会写出一个有问题的集成,而故障会出现在远离那个导致问题的过时页面的地方。带上具体信号:抽取几个你们使用最多的接口,把已发布的参考文档与真实契约进行差异对比,看看每一个偏离了多远。一旦发现偏离,就把参考文档接入构建流程,让它在每次变更时重新生成,并淘汰那份手工维护的副本。在企业和政府环境中,接口的使用者跨越团队、供应商,以及你从未见过的合同边界,一份权威的、自动生成的参考文档,往往是唯一能阻止集成方按照虚构内容进行开发的东西。

  5. 每一份高价值文档由谁负责,如果今天有一份过时了,你们会如何察觉? 本章把过时的文档当作一个缺陷来对待,并警告说,无人负责的文档会腐坏,因为更新它不是任何人的分内之事,而被当作最新内容呈现的过时文档,会摧毁人们对你们全部文档的信任。在规模化场景下,危险不在于某一个页面出错,而在于信心的缓慢侵蚀:一旦读者被过时的指示坑过一次,他们就会不再信任整个文档库,转而重新回去打扰别人。带上你们最关键文档的责任归属图,并诚实回答腐坏是如何被察觉的()是通过复审节奏、生成机制、针对系统的测试,还是纯粹靠运气。为每一份重要的文档指定一名具名负责人,优先选择那种由生成或测试维持的”活”文档,这样过时会通过机制自动显现,而不是靠一个尴尬的读者来发现。对于那些比最初团队更长寿的企业和政府系统而言,无人负责的文档是一项负债,审计人员或接手的供应商迟早会为此向你收取代价。

  6. 你们的文档有多容易被发现,还有多少关键知识仍然只存在于聊天记录和人们的脑子里? 本章指出,找不到的知识等同于不存在,并警告不要让文档碎片化地散布在太多互不相连的维基和工具之中,同时敦促你在隐性知识溜走之前,把它捕获成持久、可查找的形式。在一个大型组织中,同一个事实常常被重新发现、重新提问、重新回答上百次,因为没有人能找到它已经被写在哪里,而每一次人员离开,都会带走不可替代的上下文。带上证据:数一数你们维护了多少个各自独立的文档归属地,试着仅凭搜索找到三个重要事实,并记录真正的答案原来藏在某个人的记忆里,还是一条被埋没的消息里。整合到一个具备真正搜索能力和清晰导航的已知归属地,并把捕获隐性知识变成工作中的常规环节,而不是一次英雄式的抢救。在公共部门和高度外包的环境中,由于系统会按合同在供应商和团队之间交接,可被发现的书面知识是唯一能在交接中幸存下来的东西。

分行业视角

初创公司。 只有为数不多的几名工程师,也没有多余的资源,只记录一次凌晨两点的停机或一位新员工真正会需要的东西:每个服务一份真实的 README、一份经过测试的、针对人人都害怕的部署和恢复流程的操作手册,以及几条带日期的笔记,记下那些你们本来会忘记的决策。从契约中生成 API 文档,这样你们就永远不用手工维护它们。抵制住构建一个文档平台的冲动;在代码旁边放一个受版本控制的标记语言文件夹,在你们真正感到痛之前就已经足够了。

小型企业。 没有技术文档撰写人员,预算也紧张,就依靠你们的工具已经能自动生成的文档,以及轻量级的代码化文档,而不是配备一个专门团队。把这个选择当作购买还是自建来考量:优先选择能自动产出自身最新参考文档和可搜索知识库的平台,而不是一个需要你手工维护的维基。把你们稀缺的精力花在那两三份一旦缺失就会让业务停摆的文档上,让一个错误或缺失的页面成为修正责任归属的触发点。

企业。 在众多团队中,问题在于一致性和可发现性:一条共享的代码化文档流水线、一套共同的结构(例如 Diátaxis)、自动生成的 API 参考文档和变更日志,以及在各处都以同样方式应用的决策记录,这样知识就不会碎片化地散布在几十个维基中。为每一份高价值文档指定负责人,并衡量准确性,而不仅仅是文档是否存在。把架构记录、操作手册和决策日志当作审计证据来对待,并把它们的产出方式标准化,这样一次控制评审找到的是一条有文档记录、经得起辩护的轨迹,而不是一场手忙脚乱。

政府。 采购规则和公众问责让文档成为一项可交付成果,而不是一种礼貌之举。把架构文档、操作手册和决策记录作为强制性产物写入合同,并接受准确性复审,这样知识才能在供应商交接之后存活下来,系统才能由任何接手的人来运维。要求任何被授权的运维人员仅凭操作手册就能响应一次事件,并把决策日志作为一份透明的公开记录,说明各项选择做出的原因。在这里,文档薄弱不是私人层面的不便;它会变成由纳税人埋单的、代价高昂的逆向工程。

示例

初创公司。 一家五人规模的初创公司为每个服务撰写了一份真实的 README,并为那个人人都害怕的部署和恢复流程写了一份简短的操作手册,这样凌晨两点的停机就不再依赖于叫醒那位唯一了解系统的创始人。他们从契约中生成 API 文档,而不是手写;还记下了几条带日期的笔记,解释他们为什么选择了这个数据库和这种身份验证方式。这一切都保持轻量,但这意味着第六和第七位新员工是从文档中上手的,而不是靠打扰所有人。

企业。 一家大型软件公司把所有文档都保存在与代码相同的仓库中,用标记语言书写,并在拉取请求中与它们所描述的变更一起接受评审。API 参考文档从服务契约中生成,因此永远不会偏离。变更日志从结构化的提交记录中生成,架构决策记录保存了重大选择背后的推理过程。一个已发布的文档站点会在每次合并时自动构建。新工程师能迅速上手,因为入职指南和操作手册是最新的、可被找到的,待命工程师依靠操作手册,而不是去呼叫最初的作者。

政府。 一家国家级机构从一个即将离开的承包商那里接手了一个系统,完全依赖文档把知识带过合同的边界。由于前一个供应商把架构文档、操作手册和决策记录当作强制性的可交付成果来维护,新团队得以在没有原作者的情况下运维和修改这个系统。而在文档薄弱的地方,该机构则面临代价高昂的逆向工程。这段经历推动了一项新政策:文档是一项合同可交付成果,要接受准确性复审,而不是被当作事后补充,操作手册必须让任何被授权的运维人员都能响应事件。

商业论证:动机、投资回报率与总拥有成本

文档带来的回报体现在更短的入职时间、更低的关键人物风险、更快的事件响应,以及在系统整个生命周期中更低的变更成本。新工程师在数天而不是数周内就能产出、待命人员依靠操作手册就能解决事件而不必层层升级、维护者在系统建成多年后依然能自信地修改它:这些都是巨大的、持续性的节省,会在一个大型组织和一个漫长的系统生命周期中不断复合累积。

文档的成本是什么?撰写和维护的投入。不写文档的成本又是什么?你要持续付出:缓慢的入职速度、反复出现的提问、关键人物瓶颈、更慢的事件恢复速度,极端情况下还会出现没有人能安全修改的系统,迫使你付出昂贵的重写或逆向工程代价。在供应商交接和审计场景中,文档的缺失可能带来直接的合同和合规成本。要向领导层论证这一点,就把入职时间、事件响应时间,以及有多少关键知识存在于个人脑子里,都量化成数字。然后把代码化文档和自动生成,定位为获得持久文档而不必承担相应维护负担的方式。还要强调,不准确的文档本身就是一项负债,所以这项投资必须包括保持它的最新状态。

反模式与陷阱

  • 把过时文档当作最新内容呈现: 会误导读者,摧毁人们对全部文档的信任。
  • 一次性写完的维基: 页面创建之后再也没有更新过,悄悄地偏离了现实。
  • 文档碎片化: 知识散落在许多工具和维基中,导致什么都找不到。
  • 混杂文档类型: 教程、参考和说明混杂在同一个页面上,对任何读者都没有好处。
  • 手工维护本可生成的内容: 手写的 API 文档,必然会偏离真实的接口。
  • 部落知识: 关键的理解只存在于人们的脑子里和聊天记录中,一旦他们离开就会失落。
  • 把文档当作事后补充: 在最后才写,如果真写的话,而不是与变更同步进行。
  • 无人负责: 没有负责人的文档会腐坏,因为更新它不是任何人的分内之事。

成熟度模型

  • 第 1 级,启动。 文档稀少、分散、过时,知识存在于人们的脑子里。现存的文档写过一次就再也没被碰过,所以一次停机或一次人员离开,就意味着要对系统进行逆向工程。
  • 第 2 级,发展。 存在一些关键文档,比如 README 和几份操作手册,但维护得并不一致,也很难找到。一些团队文档写得很好,另一些几乎什么都不写,对于一个仓库应该包含什么、应该存放在哪里,也没有共同的预期。
  • 第 3 级,标准化。 代码化文档在整个组织中是常态:一套共同的结构(例如 Diátaxis)、自动生成的 API 参考文档和变更日志、决策记录,以及在评审中一项”文档要随所描述的代码一起变更”的预期。每一份高价值文档都有一位具名负责人,并且有一个具备真正搜索能力的已知归属地。
  • 第 4 级,管理。 文档被度量,而不仅仅是存在。你跟踪基本文档的覆盖率、文档变更率相对于代码变更率的比例、入职时间、仅凭操作手册解决事件的比率,以及相对于一个明确的过时阈值的新鲜度,并对照基线复审这些指标。腐坏通过生成机制、针对系统的测试,以及链接和准确性检查被机械地捕获,过时的页面基于证据被标记或剔除,而不是靠运气。
  • 第 5 级,编排。 文档在整个组织范围内持续改进并实现集成:“活的”,大部分由系统生成或针对系统测试,有人负责,可被发现,且能自适应。指标会反馈到你的投入方向,捕获隐性知识成为工作中的常规环节,随着系统、团队和读者的变化,整个文档库会被主动地重新平衡和修剪。

讨论思路

  • 如果哪一份文档明天消失了,会给你们的组织造成最大的伤害,它现在存在吗,是否保持最新?
  • 你们如何让更新文档成为修改代码时的自然组成部分,而不是一项额外的杂务?
  • 在哪些地方,你们可以用与真实来源绑定的生成式文档,取代手写文档?
  • 你们如何衡量自己的文档是否准确、是否真的被使用,而不仅仅是是否存在?
  • AI 助手应该如何改变你们撰写、维护和搜索文档的方式,它们又可能在哪些地方引入看似合理实则错误的内容?
  • 你们如何在掌握隐性知识的人离开之前,把这些知识捕获下来?

关键要点

  • 把文档当作代码来对待:版本化、经过评审、紧靠源代码,并自动发布。
  • 按读者需求,用教程、操作指南、参考和说明来组织内容。
  • 维护高价值的基本文档:README、操作手册、架构文档、入职指南和决策记录。
  • 生成 API 文档和变更日志,让它们无法偏离真实来源。
  • 用责任归属、评审预期和内容修剪来对抗腐坏;不准确的文档比没有文档更糟。

参考资料与延伸阅读

  • Daniele Procida, Diátaxis documentation framework
  • Andrew Etter, Modern Technical Writing
  • Anne Gentle, Docs Like Code
  • Google, Developer Documentation Style Guide and Season of Docs guidance (as reference exemplars)
  • Michael Nygard, Documenting Architecture Decisions (architecture decision records)
  • Andrew Hunt and David Thomas, The Pragmatic Programmer (on knowledge and documentation)
  • Keep a Changelog (as a reference convention)