代码 Review 的艺术——既让对方接受修改,又不伤害关系
代码 Review 的艺术——既让对方接受修改,又不伤害关系
适读人群:有代码 Review 经历的工程师,特别是需要给别人做 Review 的 Senior 和 Tech Lead | 阅读时长:约16分钟 | 核心价值:一套真正有效的 Code Review 沟通方法,让 Review 既提升代码质量,又维护团队关系
Code Review 是软件开发中最有价值的工程实践之一,也是最容易造成人际摩擦的一个环节。
我做了几年的 Code Review,既作为 reviewer 给别人 review,也作为被 reviewer 的人接受别人的 review。期间我犯过很多错,也看到别人犯了很多错,有些错导致了好几天的冷战,有些错让优秀的工程师开始考虑离职。
今天想聊的,不是如何发现代码里的技术问题(这个比较容易),而是如何在 review 中既解决问题,又不伤害关系。
为什么 Code Review 容易出问题
代码对工程师来说,不只是一堆文字,而是他的思维的外化、他工作努力的结晶。当你批评一段代码,工程师很容易感受到"你在批评我这个人"。
这是人类心理的自然反应,不是弱点,是普遍现象。
如果 reviewer 没有意识到这一点,以为在"客观地分析代码",那他的评论会让被 review 者感到被攻击、被否定,产生防御性反应,而不是开放地接受建议。
另一个来源:权力不对等。
当一个高级工程师 review 一个初级工程师的代码,双方对修改建议的感受是不同的。高级工程师觉得"我只是在提技术意见",初级工程师可能感受到"我必须改,否则 PR 不会被通过,我的工作会受到影响"。这种权力差异,会让 review 的压力感倍增。
基本原则:Review 的是代码,不是人
这个原则很多人都知道,但落实起来非常难。
一个具体的测试:你的 Review 评论里,有没有用第二人称"你"?
"你这里应该用 StringBuilder 而不是字符串拼接。"→ 对人 "这里字符串拼接在循环里会有性能问题,可以改成 StringBuilder。" → 对代码
区别只是措辞,但感受完全不同。前者隐含了批评,后者是陈述一个事实和建议。
我有一个简单的规则:在 Review 评论里,尽量不用"你"。描述问题时,主语是"这段代码"、"这个方法"、"这里",不是"你"。
分清必须改和可以改
Code Review 中有一个很重要但很多人忽略的区分:哪些评论是"这个必须改,否则 PR 不能合并",哪些是"这个可以考虑改,但我不是强制要求"。
如果你把所有评论(包括个人偏好类的建议)都以同等语气提出,被 review 的人会感到压力巨大,因为他不知道哪些是必须改的,哪些是可以权衡的。
我通常用前缀来区分:
- [MUST] 或 [Block]:这个必须修改才能合并(安全漏洞、明显的 Bug、违反核心设计规范)
- [Suggest] 或 [Nit]:建议考虑,但不强制(可以做得更好,但现在也可以接受)
- [Question]:我有疑问,想理解一下(不是要求修改,是要求解释)
- [Optional]:个人偏好,你决定(我觉得这样更好,但如果你有理由不改,我可以接受)
这样分类之后,被 review 的人能清晰地知道哪些是阻塞项,哪些是参考意见,极大地减少了沟通的歧义。
提问,不要命令
"这里应该用工厂方法,不要直接 new。" ← 命令 "这里为什么选择直接 new 而不是用工厂方法?是有什么具体的考量吗?" ← 提问
两种方式,技术观点是一样的,但后者:
- 给了对方解释的机会。也许他有你不知道的理由。
- 没有假设对方一定是错的,降低了防御性反应。
- 如果对方确实没有考虑清楚,这个问题会引导他自己发现问题,比你直接告诉他答案有更好的学习效果。
我在 Review 时,几乎把所有"你应该xxx"都改成了"为什么这里xxx,有没有考虑过yyy?"这一个改变,让我的 Review 评论的接受率明显提高,因为对方感觉是在讨论,而不是在被评判。
解释背后的原因,不只是说什么是对的
"这里应该用 Optional 而不是返回 null。" ← 只说结论 "这里返回 null 的话,调用方需要记得做 null 检查,容易引发 NullPointerException。改成 Optional 可以让编译器强制调用方处理'没有值'的情况,从语法层面避免这类 Bug。" ← 说了理由
后者有两个好处:
- 被 review 的人真正理解了为什么,下次不需要提醒就会记得。
- 被 review 的人可以基于理由来反驳(如果他认为你的理由在这个场景下不成立)。
光说结论的 Review 评论,往往会让人感觉是在服从权威而不是在学习。解释理由,才能让 Review 变成一个真正的学习过程。
先认可,再提建议
人类的大脑在受到认可时,对后续建议的接受度会更高。这不是什么心理学技巧,是基本的沟通常识。
在做 Review 时,如果一个 PR 里有做得好的地方,主动点出来。
"这里用了策略模式处理不同渠道的支付,我觉得设计很清晰,扩展性好。" → 然后再提修改建议
这不是在"哄人",而是在做一个完整的 Code Review:好的地方值得被看见,不只是问题需要被指出。
当一个 PR 整体质量不错时,reviewer 只回复问题,没有任何正面反馈,被 review 的人会感到沮丧——"我花了这么多时间做这件事,换来的只有一堆问题"。这种感受积累多了,会影响团队的士气。
同步沟通解决复杂问题
文字 Review 有一个根本的局限:缺乏表情和语气,很容易被误读。
当 Review 评论超过3轮来回、或者涉及架构层面的争议时,切换到同步沟通(语音/视频/线下)往往更高效。
我的原则是:在文字 Review 中,如果感觉要打一段超过100字的评论,就切换到面对面或者语音,5分钟说清楚的事情,文字来回可能要30分钟还说不清楚。
作为被 Review 者:如何优雅地接受和反驳
不只是 reviewer 需要技巧,被 review 的人同样需要。
接受建议: 如果你同意修改建议,简短确认,然后修改。不需要过度道歉("不好意思我写得太差了"),也不需要过度解释("当时这样写是因为……")。
反驳建议: 如果你不同意修改建议,解释你的理由,而不是沉默或者消极执行。
"我理解你的建议,但这里我选择直接 new 而不用工厂,是因为这个对象在整个系统里只在这一个地方被创建,引入工厂模式会增加复杂度但没有明显好处。如果你认为未来有可能在其他地方创建,我可以加上,但目前我倾向于保持简单。你怎么看?"
这种反驳是专业和有效的,因为它:
- 承认了对方的观点是有道理的
- 解释了自己的理由
- 开放地邀请对方进一步讨论,而不是把问题悬在那里
不要沉默地改。 如果你不理解为什么要改,要问。被动执行你不理解的修改建议,短期看起来"好说话",长期来看你什么都没学到,而且下次还会犯同样的"错误"。
一个我犯过的严重错误
工作的第三年,我给一个比较资深的同事 review 代码,当时我用了这样的语气:
"这个设计不对,应该用 XXX 方式来实现,理由是 YYY。"
对方在评论下面只回了三个字:"知道了。"
后来发现,这个对方后来在一段时间内都不太和我合作,有问题也不主动来找我。
后来一次 1:1,他说了一句话让我印象很深:"Code Review 应该是讨论,不是判决。"
那个经历让我彻底反思了自己的 Review 风格。我后来专门和他聊了,承认了我当时的语气有问题。那次对话之后,我们的关系完全修复了,而且他给了我很多 Review 方面的建议。
技术水平再高,如果 Review 方式造成了人际摩擦,对团队的净损失是负的。
Code Review 的速度与质量平衡
工程师做 Review 时有一个常见的两难困境:认真 Review 很花时间,但不认真 Review 又失去意义。
我的原则是:Review 的深度要和 PR 的风险匹配。
高风险 PR(需要认真 Review):
- 修改核心业务逻辑(支付、订单、权限)
- 数据库 schema 变更
- 影响多个服务的接口变更
- 引入新的第三方依赖
这类 PR,花 30-60 分钟认真 Review,是值得的。
中等风险 PR(适度 Review):
- 新功能开发
- Bug 修复(影响范围有限)
这类 PR,看整体逻辑是否合理,重点关注可能的边界 case,20-30 分钟。
低风险 PR(快速 Review):
- 文档更新
- 样式和格式调整
- 配置更新(如果修改合理)
这类 PR,快速确认没有明显问题,5-10 分钟即可。
把 Review 时间花在值得的地方,能让 Review 既保持质量,又不成为开发速度的瓶颈。
建立团队的 Review 文化
Code Review 的质量,很大程度上取决于团队整体的 Review 文化,而不是某个人的个人行为。
好的 Review 文化的标志:
- 大家都愿意提出问题,不担心被认为是"挑毛病"
- 大家都愿意接受 Review,把 Review 看作学习机会
- Review 评论有来有往,是真正的讨论而不是单向命令
- 好的代码会被点赞和称赞,不只有问题才被提及
作为团队中有经验的工程师,你能做的最重要的一件事:以你希望别人对你 Review 的方式,去 Review 别人的代码。这种以身作则,比任何规定都更有说服力。
