每个工程师都经历过这种情况:你发现了一个很有前景的开源库,导航到文档页面,却发现只有一份简陋的 README,API 参考不完整,”入门指南”假设你已经理解了整个系统。你花了两个小时阅读源代码来回答一个本应两分钟就能解决的问题。然后你转向了文档更好的竞争对手。
文档不是可有可无的东西。它是一种竞争优势——对于争取采用的开源项目,对于争取开发者的公司,以及争取组织关注和资源的内部团队而言。然而,大多数工程组织将文档视为事后考虑,是在”真正的工作”完成后才需要完成的任务。本文旨在论证文档就是真正的工作,并提供了一个实用的框架来做好文档工作。
为什么大多数文档会失败
在讨论如何编写好的文档之前,值得先了解为什么大多数文档质量很差。原因在于结构问题,而非个人问题。
错误的激励机制:工程师因交付功能而获得奖励,而非因编写文档。绩效评估很少提及文档质量。晋升标准侧重于技术复杂度和影响力,而文档工作难以展示这些方面。
错误的时机:文档通常在实现完成后编写,此时工程师的理解度最高,但动机却最低。功能已经交付,PR 已经合并,为他们刚刚构建的东西编写文档感觉像是在做无用功。
错误的受众模型:工程师为已经理解系统的人编写文档——也就是他们自己和团队成员。他们跳过基础概念,假设新读者不具备所需的上下文,关注”是什么”而非”为什么”。
没有所有权:文档人人有责,就无人负责。没有明确的所有权和维护流程,文档会迅速过时。在重大重构后的几个月内,大部分文档就会过时或具有误导性。
错误的格式:许多组织默认使用 wiki 或 Google Docs,这些工具与代码库是分离的。与代码分离的文档不可避免地会与代码产生分歧。
代码即文档
将文档作为代码的方法以与源代码相同的严谨态度对待文档:它存在于版本控制中,经过代码审查,由 CI 测试,并自动部署。这不仅仅是一种组织偏好——它从根本上改变了代码与文档之间的关系。
# 文档与代码共存
project/
src/
auth/
login.ts
login.test.ts
api/
users.ts
users.test.ts
docs/
getting-started.md
guides/
authentication.md
api-reference.md
reference/
configuration.md
docusaurus.config.js
package.json
当文档与代码位于同一仓库中时,几件事会自然而然地发生。改变行为的拉取请求包括文档更新——审查者可以在同一个差异中看到两者的变更。文档与版本一同进行版本控制。断开的链接和过时的引用可以被 CI 捕获。更新文档的障碍与更新代码的障碍相同——一个拉取请求。
实际实现很简单:文档用 Markdown(或 MDX)编写,存储在仓库中,由静态站点生成器构建,并与应用程序一起部署。文档的变更与代码变更经历相同的审查过程。
Diataxis 框架
大多数组织能为文档做出的最具影响力的改进是采用 Daniele Procida 创建的Diataxis 框架。Diataxis 确定了四种不同类型的文档,每种都有不同的用途和需要不同的方法。
教程:以学习为导向
教程是通过一系列步骤引导读者完成项目的课程。它们以学习为导向——目标不是完成任务,而是通过实践来学习。一个好的教程就像烹饪课:学生跟随操作,最终得到一个可运行的结果,并在过程中获得理解。
教程的关键原则:
- 读者应该在结束时有所收获
- 每个步骤都应该可行 — 定期测试你的教程
- 不要解释一切 — 专注于实践,而非理解
- 从最简单的例子开始,逐步构建复杂性
## 教程:构建你的第一个 API 端点
在本教程中,你将创建一个用于存储和检索用户资料的 REST API 端点。
结束时,你将有一个在本地运行的可用 API,可以使用 curl 进行测试。
### 前提条件
- 已安装 Node.js 20 或更高版本
- 一个文本编辑器
### 步骤 1:创建项目
创建一个新目录并初始化项目:
```bash
mkdir my-api && cd my-api
npm init -y
npm install express
```
### 步骤 2:创建服务器
创建一个名为 `server.js` 的文件,内容如下:
```javascript
const express = require("express");
const app = express();
app.use(express.json());
const users = new Map();
app.post("/users", (req, res) => {
const { name, email } = req.body;
const id = crypto.randomUUID();
users.set(id, { id, name, email });
res.status(201).json(users.get(id));
});
app.get("/users/:id", (req, res) => {
const user = users.get(req.params.id);
if (!user) return res.status(404).json({ error: "Not found" });
res.json(user);
});
app.listen(3000, () => console.log("Running on port 3000"));
```
操作指南:任务导向
操作指南是完成特定任务的”食谱”。与教程不同,它们假设读者已经具备基础知识,需要解决特定问题。操作指南就像烹饪书中的食谱 — 它假设你知道如何烹饪,并告诉你如何制作特定的菜肴。
操作指南的关键原则:
- 标题应描述所解决的问题:”如何配置 SSO 与 SAML”
- 假设读者具备能力 — 不要重新解释基础知识
- 专注于特定任务 — 不要偏离主题进行解释
- 提供完整的解决方案,包括边缘情况
参考文档:信息导向
参考文档描述系统的实际状况 — API、配置选项、CLI 标志、错误代码。它是信息导向的, meant to be consulted rather than read. 好的参考文档就像字典:全面、准确且结构一致。
参考文档的关键原则:
- 要全面 — 每个公共 API、每个配置选项、每个错误代码
- 要一致 — 每个条目使用相同的结构
- 要准确 — 与实际行为不符的参考文档比没有文档更糟糕
- 尽可能自动生成 — 从代码注释生成的 API 参考文档能保持同步
解释:理解导向
解释是帮助读者理解概念、建立联系和构建思维模型的讨论。它们以理解为导向——回答”为什么”的问题。一个好的解释就像一篇关于烹饪的杂志文章:它讨论技术、历史和原则,而不告诉你做任何具体的事情。
解释的关键原则:
- 讨论”为什么”——设计决策、权衡考虑、备选方案
- 提供背景——这如何融入更大的图景中?
- 使用类比和示例建立直觉
- 可以表达观点——解释系统为何以这种方式工作
API 文档最佳实践
API 文档需要特别关注,因为 API 是开发者与您的系统交互的主要接口。糟糕的 API 文档直接损害了开发者的生产力。
从 OpenAPI 开始
每个 REST API 都应该有一个 OpenAPI(前身为 Swagger)规范。这是 API 合约的单一事实来源。从这个规范中,您可以生成交互式文档、客户端库、模拟服务器和测试套件。
# openapi.yaml
openapi: 3.1.0
info:
title: 用户管理 API
version: 2.1.0
description: |
管理用户账户和配置文件的 API。
所有端点都需要 Bearer token 认证。
paths:
/users:
post:
summary: 创建新用户
description: |
创建新用户账户。电子邮件必须在组织内的所有账户中唯一。
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserRequest"
example:
name: "Jane Smith"
email: "jane@example.com"
role: "editor"
responses:
"201":
description: 用户创建成功
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"409":
description: 电子邮件已存在
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: "EMAIL_EXISTS"
message: "使用此电子邮件的用户已存在"
每个端点都需要示例
任何 API 文档中最有价值的补充就是为每个端点提供可工作的示例。不是模式描述——而是开发者可以复制粘贴的实际请求和响应体。包括成功案例、常见错误案例和边缘情况的示例。
记录错误
错误文档是大多数 API 文档的短板。每个 API 都会返回错误,开发者会花费不成比例的时间来调试它们。请记录每个错误代码、其产生原因以及解决方法。遇到错误的开发者应该能够在 30 秒内从您的文档中找到解决方案。
文档工具
文档工具领域已经显著成熟。最好的工具结合了开发者友好的编写体验和精美的输出效果。
Mintlify
Mintlify 已成为面向开发者公司的领先文档平台。它提供了精美的默认设计、基于 OpenAPI 规范的内置 API 参考生成、搜索功能、分析功能和 AI 驱动的特性。Mintlify 使用 MDX(带有 JSX 的 Markdown),允许在文档页面中包含交互式组件。其”建议编辑”功能和内置的反馈机制有助于发现文档中的不足。
Docusaurus
来自 Meta 的 Docusaurus 仍然是最流行的开源文档框架。它基于 React,支持版本控制、国际化以及丰富的插件生态系统。Docusaurus 是开源项目的默认选择——其灵活性和零成本使其适用于任何规模的项目。权衡之处在于,它比托管式替代方案需要更多的配置和设计工作。
Starlight
基于 Astro 构建的 Starlight 是最新的有力竞争者。它提供出色的性能(默认为静态 HTML,仅在需要时使用 JavaScript)、内置搜索、自动导航生成和简洁的默认主题。Starlight 特别适合那些想要优秀文档但又不想承诺使用 React 生态系统的项目。
// astro.config.mjs - Starlight 配置
import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight";
export default defineConfig({
integrations: [
starlight({
title: "My Project Docs",
social: {
github: "https://github.com/myorg/myproject",
},
sidebar: [
{
label: "Getting Started",
items: [
{ label: "Introduction", link: "/getting-started/intro" },
{ label: "Installation", link: "/getting-started/install" },
{ label: "Quick Start", link: "/getting-started/quickstart" },
],
},
{
label: "Guides",
autogenerate: { directory: "guides" },
},
{
label: "API Reference",
autogenerate: { directory: "reference" },
},
],
}),
],
});
衡量文档质量
无法衡量就无法改进。文档质量指标可分为几个类别。
覆盖率指标:公共文档化 API 的比例是多少?有多少功能有教程或操作指南?错误代码是否已文档化?覆盖率可以部分自动化——脚本可以将 API 表面与文档条目进行比较。
时效性指标:每个页面最后更新是什么时候?有多少页面自上次重大发布以来未更新?过时的文档实际上是有害的——它比没有文档更糟糕,因为它会产生误导。
使用指标:哪些页面访问量最高?人们在搜索什么?他们在哪里离开文档(表明他们没有找到所需内容)?Mintlify 和 Docusaurus 等工具提供内置分析功能。
反馈指标:在每个页面添加”这有帮助吗?”小部件。跟踪正面与负面反馈的比例。跟进负面反馈以确定具体差距。这些简单的小部件能产生出人意料的有效数据。
支持关联:跟踪有多少支持工单引用了文档——包括”我在文档中找不到它”和”文档是错误的”。与文档相关的支持工单减少是文档改进的直接衡量标准。
创建文档文化
工具和框架是必要的,但还不够。维持高质量的文档需要文化转变。
将其作为一流成果:文档应该是每个功能的必需交付成果,而不是可选的后续工作。添加功能但没有文档的拉取请求不应被批准。这需要成为通过代码审查执行的团队规范。
分配所有权:每个文档部分都应有指定的所有者。当服务交接时,文档所有权会明确转移。没有所有者的文档会逐渐退化。
认可这项工作:将文档贡献纳入绩效评估、冲刺回顾和团队庆祝活动中。如果编写文档是隐形工作,人们就会停止做这件事。
降低门槛:尽可能简化文档更新。一键编辑链接、常见文档类型的模板和明确的贡献指南可以减少摩擦。文档贡献过程中的每个不必要的步骤都是某人不愿参与的理由。
安排维护:定期安排文档审查和更新时间。一些团队指定轮值的”文档警长”,他们在每个冲刺中花一天时间审查和更新文档。其他团队则安排季度文档冲刺。机制不如承诺重要。
优秀文档示例
向最好的学习是有指导意义的。几个项目为开发者文档设定了标准。
Stripe 被广泛认为是行业内拥有最佳 API 文档的平台。每个端点都提供了多种语言的可运行示例。错误消息直接链接到相关文档。引导体验能让开发者在五分钟内从零开始完成第一次 API 调用。
Anthropic 提供清晰、组织良好的 API 文档,包含实用示例、关于提示工程的详细指南,以及对模型能力和限制的透明解释。该文档在技术精确性和可访问性之间取得了平衡。
Tailwind CSS 展示了参考文档如何做到既全面又设计精美。每个工具类都有视觉示例进行文档说明,搜索体验也异常快速。
Rust 拥有《Rust 编程语言》(通常被称为”The Book”),这被广泛认为是史上最佳编程语言教程之一。它以精心设计的结构引导读者从基础原理到高级概念。
Django 是最早认真对待文档的项目之一,其文档至今仍是黄金标准。教程、操作指南、参考和说明之间的清晰分隔早于 Diataxis 框架——事实上,正是这个框架启发了 Diataxis。
文档作为竞争优势
证据很明确:文档完善的项目和产品更胜一筹。在开源领域,文档质量是核心功能之后采用率的最强预测指标。在企业软件中,文档质量直接影响实现价值和所需支持的成本。在内部平台中,文档决定了团队是自助服务还是提交工单。
那些将文档视为一流工程成果的组织——给予与代码同等的关注、工具和尊重——其表现始终优于那些将其视为事后考虑的组织。文档不是负担,而是杠杆。在优质文档上投入的每一小时,都能节省开发者困惑、支持工单方向错误和评估被放弃所浪费的十小时。
从 Diataxis 框架开始。采用文档即代码。衡量质量。建立文化。您的文档是您产品的第一印象,是持续的用户体验,也是最具可扩展性的支持形式。让它配得上所代表的工程价值。
