写文档
何时用
- 新建一个库、工具或服务,需要写 README。
- 现有文档与代码脱节,需要更新。
- 接到"补充文档"的任务,不确定该写什么、写多少。
- 写内部技术方案或 API 参考文档。
核心规则
1. 开头讲"这是什么、解决什么问题、给谁用",30 秒能判断要不要继续读
规则: 文档第一屏必须回答三个问题:这个东西是什么、它解决了什么具体问题、目标读者是谁——不废话,不卖关子。
为什么: AI 写文档时惯于先铺一大段背景介绍和设计理念,把"这是什么"埋在第三段。读者在 30 秒内判断不了这个东西是不是自己需要的,直接关掉。常见事故:README 开头一段"现代分布式系统面临的挑战……",读到第五段才出现一句"本库用于…"——用户早已离开。
怎么做:
- 第一行:一句话说清是什么。
xxx 是一个用于 yyy 的 zzz 工具。 - 第二段:说清它解决什么痛点,以及不解决什么(边界)。
- 第三段或 badge 区:目标用户(前端?后端?DevOps?)、语言/运行时要求。
- 整个"是什么"部分控制在 5-8 行以内。
2. 快速开始可复制即用:安装命令、最小示例,真实可跑
规则: "快速开始"章节必须包含可直接复制执行的安装命令和最小完整示例,运行后能看到预期输出。
为什么: AI 写的"快速开始"常用伪代码或省略关键步骤:用 <your-api-key> 占位符但没说去哪里拿,import 路径和实际包名对不上,示例依赖某个环境变量但没说明。读者跟着做一遍跑不起来,信任立刻崩塌。文档最大的用途就是让人第一次能跑通——跑不通的文档比没文档更打击信心。
怎么做:
- 安装命令给出完整版本(
npm install xxx@2.1.0或pip install xxx==1.5.0)。 - 示例代码能"无脑复制到空项目里跑通",不依赖未说明的前置条件。
- 如果有必填的环境变量或配置,在示例旁边紧接着给出怎么获取/生成的说明。
- 文档发布前自己跑一遍快速开始章节,确认没有步骤缺失。
3. 结构按读者需求组织(上手→用法→进阶),不按代码结构
规则: 文档目录顺序应遵循读者的使用旅程:从快速上手到常见用法到高级配置,不要按照代码文件/模块的组织方式排列。
为什么: AI 生成文档时容易"按代码写文档"——每个 class 一个章节,每个方法一条记录,按字母序排列。这是 API reference 的写法,不是入门文档的写法。结果:新用户找不到"我应该先做什么",所有内容平铺在同一层级,没有优先级感。常见事故:一份有 30 个章节的 README,读者需要的"基本使用"在第 17 章。
怎么做:
- 固定骨架:
简介 → 快速开始 → 常见用例 → 配置参考 → 常见问题 → 贡献指南。 - 把 90% 的用户只需要一次的内容(部署、迁移、高级配置)放到"进阶"或单独页面。
- API reference 独立一份,不要混在入门文档里。
4. 示例胜过描述;术语一致,避免内部黑话
规则: 能用代码示例说明的,不用长段文字描述;全文使用统一术语,不造自己发明的词。
为什么: AI 写文档时爱用"该组件通过注册策略模式实现了可扩展的生命周期钩子机制"这类内部黑话——只有写代码的人知道"策略模式"和"生命周期钩子"在这里指什么。外部读者完全无法映射到自己的使用场景。而一个具体的代码示例,10 行能传递 3 段文字无法表达的信息量。
怎么做:
- 凡是涉及"如何使用",优先给代码示例,文字作为辅助说明。
- 术语首次出现时给一句通俗解释:
钩子(hook)——在特定生命周期节点被自动调用的回调函数。 - 不用内部代号、项目昵称、公司方言,假设读者是第一次接触这个项目的外部人员。
5. 与代码同步更新,过期文档比没文档更糟
规则: 每次改动影响到 API 或使用方式时,必须同步更新对应文档;过期或错误的文档要删除或标注,不能留着误导读者。
为什么: AI 实现新功能时经常忘记更新 README 和示例代码。结果是新用户照着文档里的旧 API 写,运行报错,以为是自己的问题。或者文档里有个"将在下一版本实现"的 TODO 留了两年,功能早实现了但文档从没更新。过期文档产生的信任成本比没文档更高——读者不知道哪些是真的,只能全部怀疑。
怎么做:
- PR checklist 里加一项:"文档是否需要更新?"(参考 PR 描述 skill)。
- 已删除的功能/API 同步从文档中删除,不要留注释说"此功能已废弃"三年。
- 对确实暂时没精力更新的部分,在文档顶部明确标注版本号和更新日期。
正例 / 反例
反例:开头铺背景、快速开始跑不通
<!-- 反例 — 开头废话,快速开始有致命缺失 -->
# MyLib
随着云原生架构的普及,开发者越来越需要高效处理异步任务。
本项目诞生于 2023 年的一次内部黑客马拉松,旨在探索……(三段背景)
## 快速开始
```python
from mylib import Client
client = Client(api_key=API_KEY) # ❌ API_KEY 哪里来的?没说
result = client.run(task) # ❌ task 是什么结构?没说
```markdown
<!-- 正例 — 开头直接,快速开始可复制即用 -->
# MyLib
**MyLib** 是一个 Python 异步任务队列客户端,用于把耗时操作卸载到后台 worker 执行。
适合需要在 Web 请求中异步处理邮件发送、图片压缩等任务的场景。
要求:Python 3.10+,需要自建或托管的 MyLib Server。
## 快速开始
1. 安装:
```bash
pip install mylib==2.3.1
获取 API Key:登录 https://mylib.example.com → Settings → API Keys → 生成新密钥。
运行最小示例:
import os
from mylib import Client, Task
client = Client(api_key=os.environ["MYLIB_API_KEY"]) # ✅ 明确说明来源
job = client.enqueue(Task(type="send_email", payload={"to": "a@b.com"}))
print(job.id) # 输出:job_abc123
---
### 反例:按模块结构组织,示例少
```markdown
<!-- 反例 — 按代码模块排列,文字描述多,示例少 -->
## ConfigLoader 类
ConfigLoader 类负责从多种数据源加载配置,支持环境变量覆盖、
类型转换、默认值注入及验证回调注册。内部采用责任链模式……
### ConfigLoader.register_validator(fn)
注册一个验证器函数。该函数接受 config dict 并返回 bool……
<!-- 正例 — 用例驱动,示例优先 -->
## 常见用法
### 从环境变量加载配置
```python
from mylib import ConfigLoader
config = ConfigLoader.from_env()
print(config.get("DATABASE_URL")) # ✅ 一看就知道怎么用
添加自定义校验
def must_have_db(cfg):
return "DATABASE_URL" in cfg
config = ConfigLoader.from_env(validators=[must_have_db]) # ✅ 示例即文档
---
## 自查清单
- [ ] 文档第一屏能在 30 秒内让读者判断这个工具是否适合自己。
- [ ] 快速开始章节的每一步我都亲自跑过,确认可以从零复现。
- [ ] 文档结构按读者旅程组织(上手→用法→进阶),不按代码模块排列。
- [ ] 关键操作用代码示例展示,没有纯文字描述却没有示例的章节。
- [ ] 没有使用只有团队内部人才懂的术语或代号。
- [ ] 本次代码改动涉及的 API 变化已同步更新到文档。
- [ ] 过时或已删除的内容已从文档中移除,没有留"废弃"标注超过一个版本周期。