大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
文档审查
这一课你将能做到
- 审查 PR 时识别出「代码改了但文档没跟上」的不一致
- 写出有用的 Javadoc / 注释,删掉废话注释
- 审查 README、CHANGELOG、API 文档是否准确、可执行、面向读者
为什么要学
代码是写给人看的,顺便能在机器上跑。文档和注释是「为什么这样写」的唯一载体——代码只能告诉你「做了什么」,不能告诉你「为什么这么做」。
人话:三个月后你自己回来看这段代码,能不能在 30 秒内搞懂当时的考量?如果不能,就是文档审查没做到位。
先建立图像
一个 PR 里「文档」的完整范围:
PR 涉及文档的 4 层
├─ 代码内注释 / Javadoc ← 解释「为什么」
├─ README.md ← 项目门面,新人能不能跑起来
├─ CHANGELOG.md / 提交信息 ← 历史与演进
└─ 外部文档(Wiki / OpenAPI) ← 契约与使用手册
审查标准:
✓ 与代码同步(改代码必须改对应文档)
✓ 面向读者(写给谁看?对方要做什么?)
✓ 可执行(命令、示例能复制粘贴跑通)
✓ 解释 Why(What 代码本身会说)
跟着做
步骤 1:审代码内注释——删除废话
❌ 反例:
// 设置课程标题
course.setTitle(req.getTitle());
// 循环章节列表
for (Chapter c : chapters) {
// 打印章节
log.info("chapter={}", c);
}
/**
* 获取课程
* @param id id
* @return 课程
*/
public CourseVO get(Long id) { ... }
✅ 正例:注释解释「为什么」和「不明显的坑」
course.setTitle(req.getTitle());
// 章节数量大时(>500)逐条打日志会刷屏,只打前 3 条用于排查
chapters.stream().limit(3).forEach(c -> log.info("chapter={}", c));
/**
* 获取课程详情(含作者与章节数)。
*
* <p>注意:课程被软删除(deleted=1)时此方法仍返回数据,
* 由上层 Controller 决定是否对 C 端隐藏,避免后台运营看不到历史数据。
*
* @param id 课程主键
* @return 课程详情;课程不存在时抛 {@link BizException} COURSE_NOT_FOUND
*/
public CourseVO get(Long id) { ... }
审查动作:凡是「把代码翻译成中文」的注释,全部删掉。凡是解释「为什么这么写」「有什么坑」「什么时候会抛异常」的注释,保留并鼓励。
步骤 2:审 Javadoc 的完整性
公开 API(Service 层、工具类、对外 SDK)必须有 Javadoc;私有方法、显而易见的 getter/setter 不强制。
/**
* 报名课程(幂等)。
*
* <p>幂等保证:同一 userId + courseId 重复调用不会产生重复记录,
* 由数据库唯一索引 uk_course_user 兜底。
*
* @param courseId 课程 ID,不能为空
* @param userId 用户 ID,不能为空
* @throws BizException 名额已满 / 课程不存在 / 重复提交
* @see CourseMapper#decreaseQuota
*/
void enroll(Long courseId, Long userId);
审查点:
- 幂等性、线程安全性是否声明?
- 边界条件(null、空集合)行为是否说明?
- 抛什么异常、什么场景抛,是否列出?
步骤 3:审 README 的「新人跑通」能力
❌ 反例:
## 启动
mvn spring-boot:run
✅ 正例:
## 环境要求
- JDK 17+
- MySQL 8.0+(本地或 Docker)
- Redis 6.0+
## 快速启动
1. 初始化数据库
mysql -u root -p < scripts/init.sql
2. 启动 Redis
docker run -d -p 6379:6379 redis:7
3. 修改配置
cp application-local.yml.example application-local.yml
# 填入你的数据库密码
4. 启动
mvn spring-boot:run -Dspring-boot.run.profiles=local
5. 验证
curl http://localhost:8080/api/v1/health
# 期望返回 {"status":"UP"}
审查动作:自己照着 README 在一个干净环境里跑一遍。跑不通就是 README 有问题。很多团队 README 是项目刚成立时写的,半年后再也没人验证过。
步骤 4:审 CHANGELOG 与提交信息
❌ 反例:
fix bug
update
优化
✅ 正例(Conventional Commits 风格):
fix(course): 修复课程名额并发超卖问题
之前用「先 count 再 insert」的写法存在 check-then-act 竞态,
并发报名时会超出 quota 上限。
改为:
- 数据库层用 UPDATE ... WHERE quota > 0 原子扣减
- 加 Redis 防重锁(10s TTL)拦截重复提交
- 保留 uk_course_user 唯一索引兜底
Closes #123
审查点:提交信息能否让半年后的人(或回滚时的运维)看懂「改了什么、为什么、影响面」。
步骤 5:审「代码改了但文档没改」
这是文档审查最常见的坑。检查清单:
| 代码改动 | 必须同步的文档 |
|---|---|
| 新增/修改接口 | OpenAPI 注解、API 文档 |
| 改默认配置 | application.yml 注释、部署文档 |
| 加新依赖 | README 环境要求 |
| 改启动流程 | README 快速启动 |
| 改数据库表 | ER 图、迁移脚本注释 |
| 改环境变量 | .env.example、部署文档 |
审查动作:对照 PR 的代码 diff,逐项检查上面这张表。
讲清楚要点
- 注释的黄金法则:代码说 What,注释说 Why。翻译代码的注释是噪音,不如删掉。
- 公开 API 必须有 Javadoc:包括幂等性、线程安全性、参数边界、抛出异常。私有实现细节可以不写。
- README 是项目门面:新人能否在 30 分钟内跑起来,是 README 质量的唯一标准。
- 文档与代码同 PR 更新:「文档下次再补」基本等于「文档永远不会补」。审查时把「代码改了文档没改」当作阻断项。
- CHANGELOG 是给未来的自己看的:线上出问题要回滚时,CHANGELOG 决定你能不能 5 分钟内判断「这次问题是不是上次改动引入的」。
你可能会懵的地方
-
注释和「代码即文档」冲突吗? 不冲突。好的命名、清晰的结构确实能减少 80% 的注释需求,但剩下 20%——业务规则、历史包袱、性能权衡、临时方案——代码本身表达不了,必须靠注释。审查时区分「代码该表达但没表达」(改名、抽方法)和「代码表达不了」(写注释)。
-
Javadoc 要不要写 @param @return? 参数名本身有语义时(
courseId、pageSize),@param可以省略;参数含义不直观(type=1是什么意思)时必须写。@return同理——返回boolean时最好写清楚 true/false 各代表什么。 -
README 要写多长? 够新人跑通即可。常见的 README 反模式:把 Wiki 内容全塞进来。README 只放「跑起来 + 项目结构 + 常用命令」,深度文档放 Wiki 并给链接。
-
提交信息有必要写那么细吗? 看团队。但至少做到:标题行说清「做了什么」(动词开头,50 字内),正文说「为什么」和「影响面」。
fix bug这种提交在线上事故复盘时会让所有人骂娘。 -
临时方案(hack)要不要注释? 必须!而且要带日期和负责人。
// TODO(zhangsan, 2026-10): 临时方案,等 XX 接口上线后重构。否则半年后没人敢动这段代码。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
// 设置标题 这种翻译式注释 | 噪音,分散注意力 | 删掉,或改为解释 Why |
| 接口改了但 OpenAPI 注解没改 | 前端拿错的契约文档 | 同 PR 更新 @Operation @Schema |
| README 启动命令过期 | 新人跑不起来,第一印象差 | 定期验证 + CI 加 README 测试 |
提交信息 fix bug | 无法回溯定位 | Conventional Commits |
Javadoc 只有 获取课程 四个字 | 等于没写 | 说明幂等性、边界、异常 |
卡点急救
-
老项目大量代码没注释,要不要补? 不要全量补(工作量爆炸且收益低)。只在你「改到这段代码时」补充——既降低单次成本,又让最常变动的代码先有文档。
-
PR 太大,文档审查抓不住重点? 让作者拆 PR。超过 400 行变更的 PR,文档审查和代码审查都会流于形式。参考 Review 流程课的「PR 大小」原则。
-
团队没人写文档,怎么推动? 从「卡 CI」开始:接入
javadoc校验、PR 模板强制填「文档更新」勾选项。先强制再养成习惯。
面试常问
-
你怎么看待「代码即文档」? 同意一半。好的命名和结构确实能让代码自解释 What,但 Why(业务背景、权衡、历史原因)只能靠注释。实战策略:公开 API 必写 Javadoc,复杂业务逻辑必有注释解释 Why,临时方案必带 TODO + 负责人 + 日期。
-
提交信息规范怎么做? 推荐 Conventional Commits:
<type>(<scope>): <subject>。type 用 feat/fix/docs/refactor/chore 等;scope 是模块名;subject 是动词开头的短句。配套工具:commitlint 卡 CI,standard-version 自动生成 CHANGELOG。 -
怎么做文档与代码同步? 三个手段:① 文档和代码在同一个仓库同 PR 评审;② CI 校验(如 springdoc 生成 OpenAPI 后和仓库里的契约文件对比 diff);③ 定期演练——新人入职第一周任务就是照着 README 跑通项目,跑不通的地方立刻提 PR 修文档。
自测三问
- 什么样的注释应该被删掉?什么样的注释必须保留?
- 改了默认配置但没改 README,会有什么问题?
- 一个合格的 Javadoc 至少应该包含哪三类信息?
小结一句话
文档审查的核心是「同步」与「面向读者」——代码改文档必改,文档写出来是给人跑通、给人看懂「为什么」的。
延伸
- Google Java Style Guide - Javadoc
- Conventional Commits 规范
- 《The Art of Readable Code》
- 下一课:测试审查——覆盖率与断言质量。