代码评审
评审不是资深开发者的专属环节。新人视角能发现老手已经习惯的隐含假设,而评审本身是学代码风格最快的途径。
评审对象是代码
评论指向代码,不指向作者。「这个函数同时做了解析和写盘」是描述,「你写得很乱」是评价。前者能改,后者只能让人防御。
| 差的写法 | 好的写法 | 原因 |
|---|---|---|
| 这里写得很乱 | 这个函数同时做了解析和写盘,能拆开吗 | 可执行 |
| 不要用全局变量 | 能把这些全局换成 config 对象吗 | 给出替代 |
| 处理 null | X 为 null 时这里会发生什么 | 提问 |
| 用常量 | 用常量方便按环境调超时 | 说明理由 |
| LGTM | 重试退避写得很干净,比原来的固定间隔好 | 具体肯定 |
评论里少用「你」,多用「这个函数」「这行」。语气差别不大,接收方的感受差别很大。
区分阻塞与建议
把「必须改」和「可以更好」分开,作者才知道优先级。常见做法是用 nit: 标小问题、question: 标疑问,再加一个明确的阻塞标记。
- 只拦真正的问题:正确性、安全、数据丢失、接口破坏
- 风格问题交给 formatter 和 linter,不要人肉纠
- 大量 nit 会淹没真正的阻塞项
- 小 nit 可以合并后自己顺手改
- 该停就停,作者的时间和耐心有限
- 不确定是否阻塞时先问,不要直接拦
- 把必须改的原因写在一句话里
- 合并前确认 CI 和测试通过
评审的目标是让代码可维护,不是把所有代码改成你习惯的样子。
怎么审
先读 PR 描述和测试,再看实现;测试告诉你作者认为什么行为重要,描述告诉你为什么改。
- 关注边界条件、错误路径、并发和命名
- 本地 checkout 跑一遍,别只看 diff
- 用 suggestion 给可直接应用的改法
- 复审只看上次之后的增量
- AI 初审能抓拼写和明显漏洞,但不懂产品约束
- 评论引用具体行或片段,避免「上面那段」
- 同类问题合并成一条,不要刷几十条
bash
gh pr checkout 123
gh pr diff 123
gh pr review 123 --comment --body "这里需要确认空输入的行为"GitHub 的 suggestion 块可以直接提交:
markdown
```suggestion
timeout = float(os.environ.get("TIMEOUT", "5"))
```复审时用 git range-diff origin/main pr-v1 pr-v2,或在平台上按「上次评审后」过滤,避免把已经看过的代码再读一遍。
评审也是知识传递:你的问题会变成对方的注释或文档,下一次就没人需要再问。
作为作者接收评审
收到一堆评论时先分类:必须改的、需要讨论的、可以不改的。逐条回应比沉默更快推进,也避免 reviewer 重复追问。
- 接受的建议用 fixup 提交,合并前再 squash
- 不认同就给出证据:测试、文档、线上数据
- 问清楚「按你说的改,边界情况怎么处理」
- 不要在评论里争论风格,交给团队规范
- 感谢具体的指出,比笼统的「收到」有用
评审是双向的。作者最了解上下文,reviewer 最可能发现盲区;把两者当成对立面,评审就会退化成形式。
控制 PR 的大小也是作者的责任。一个几百行的 PR 可以认真读完,一个几千行的 PR 只能扫过,评审质量在打开页面时就决定了。
评审的时机
评审延迟会直接阻塞作者。攒够一批再看,作者可能在等待中切换到别的任务,上下文全丢。
- 小 PR 当天看,大 PR 先给方向性意见
- 每天固定时段处理评审,避免被消息打断
- 发布前不要才开始评审,问题会堆在一起
- 作者催进度时先给预计时间,不要沉默
- 评审完给一句总结,点明剩余风险
评审不是额外工作,它是交付流程的一部分。把它排进日程,比靠空闲时间处理更可靠。