跳转到内容

编写与维护文档

写文档是程序员最不爱做的事——但读文档是程序员最爱抱怨别人没做的事。

矛盾的根源是:写文档是离代码很远的活,代码改了文档没跟上,文档就成了谎言。Claude Code 把这件事变得不那么痛苦,因为它直接读代码——代码变了,让它重读再写一遍就行。

第一招:让 Claude 根据代码生成文档

Section titled “第一招:让 Claude 根据代码生成文档”

最直接的用法——告诉它要写什么、给谁看:

update the README with installation instructions. read package.json and the actual setup scripts. don't document what the README currently says — document what the code actually requires

最后一句「document what the code actually requires」是关键——它逼 Claude 以代码为准而不是「以现有 README 为准」。这是文档腐败的常见原因:README 是半年前写的,依赖变了、命令变了,但没人更新;如果你让 Claude「基于现有 README 优化」,它会接着编;如果让它「以代码为准」,它会去查 package.jsonscripts/Dockerfile,写出来的才是真的。

更全面的版本:

rewrite the README based on what's actually in the repo:
- installation steps: read package.json scripts and any setup.sh
- usage examples: read the CLI entry point and extract real flags
- architecture overview: read src/ structure and summarize modules
- contributing: read CONTRIBUTING.md if exists, else infer from .github/
don't include any step that's not backed by a file in the repo

「must be backed by a file」是文档可信度的护身符。

第二招:API 文档从代码注释提取

Section titled “第二招:API 文档从代码注释提取”

写 API 文档最痛苦的是重复劳动——你已经在代码里写了 JSDoc/docstring,还要再抄一遍到文档站点。让 Claude 代劳:

extract API documentation from the JSDoc comments in src/api/. generate a single docs/api.md file organized by module. for each exported function: signature, parameters, return type, and a usage example derived from the implementation

「usage example derived from the implementation」这一句很妙——它让 Claude 从代码本身推断示例,而不是凭空编一个。示例编错是 API 文档最致命的问题——用户照着跑跑不通,比没文档还糟。

如果项目用 TypeScript,可以更狠:

read the type definitions in src/types/ and the function signatures in src/api/. generate API docs that include the full TS types, parameter constraints (from JSDoc @param), and error cases (from thrown Errors)

它会综合类型签名 + JSDoc + 实际 throw 语句,写出比单一来源更完整的文档。

第三招:Markdown 格式化 Hook——自动加语言标签

Section titled “第三招:Markdown 格式化 Hook——自动加语言标签”

文档质量里有个小但致命的细节:代码块的语言标签

```
const x = 1
```

vs

```ts
const x = 1
```

后者有语法高亮,前者没有。差别巨大,但写的人总忘。挂个 Hook 自动补:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write $CLAUDE_FILE_PATHS"
}
]
}
]
}
}

Prettier 会把 Markdown 格式统一,代码块语言标签补齐。如果你想更精细——只补语言标签,不动其他格式——可以写个小脚本:

infer-lang.sh
#!/bin/bash
# 给 Markdown 代码块补语言标签(基于内容启发式判断)
file="$1"
# 简化版:用 file 命令推断,或基于关键字(import/export => ts, def => py, func => go)
# 实际项目里建议用 markdownlint 或 dprint
npx dprint fmt "$file"

PostToolUse 时跑一遍,文档永远格式整洁。

第四招:维护 CLAUDE.md——/init 生成 + 定期 review

Section titled “第四招:维护 CLAUDE.md——/init 生成 + 定期 review”

CLAUDE.md 是项目的「给 AI 的说明书」——它告诉 Claude 这个项目的命令、风格、约定。维护它和维护 README 一样重要,但更容易被忽视。

新项目第一件事:

/init

Claude 会扫描项目结构、读 manifest、看现有代码,生成一份初始 CLAUDE.md。包含:

  • 项目概述
  • 常用命令(build、test、lint)
  • 代码风格(从 .eslintrc / .prettierrc 推断)
  • 项目结构说明

CLAUDE.md 不是写一次就完事。随着项目演进,命令会变、风格会变、模块会重组。建议每月或大改动后让它自己 review 一遍:

review the current CLAUDE.md against the actual repo state. flag any:
- command that no longer exists in package.json
- style rule that contradicts .eslintrc or .prettierrc
- module mentioned in CLAUDE.md that's been renamed or removed
- missing entry for new modules added since last update
propose a diff, don't apply it yet

「propose a diff, don’t apply it yet」很重要——让 Claude 只建议不直接改。你审计 diff,决定哪些采纳。直接让它改可能引入它自己脑补的内容。

死文档比没文档还糟——它误导人。让文档「活」的最好方式是让代码成为唯一真相

generate a docs/commands.md page that lists all CLI commands. read the actual command definitions in src/cli/commands.ts — don't reuse any prior doc. each command entry must include: name, flags, example invocation

「don’t reuse any prior doc」这一句让 Claude 完全从代码生成——不污染、不继承旧错误。建议把这类「代码生成文档」步骤加进 CI,每次发版前重跑一遍,文档永远跟得上代码。

把上面几步串成工作流:

1. /init # 首次生成 CLAUDE.md
2. every release:
a. regenerate API docs from code
b. re-extract README installation steps from package.json
c. ask Claude to review CLAUDE.md against current repo
3. before merge:
a. if a public API changed, regenerate the relevant docs
b. if a command was renamed, update README and CLAUDE.md

走完这套流程,文档不会再是「半年前写的,已经过时」的状态。