如何高效地做代码审查:一位资深工程师的20年经验总结
原文作者:Matthias Endler
我从事代码审查工作已经超过二十年了。如今,我大约50%到70%的工作时间都在以某种形式审查代码,这也是我主要的职责之一,与系统设计并列。
随着时间推移,我逐渐摸索出一套高效的代码审查方法。现在的我,关注的重点与刚入行时已大不相同。
一、从“大局”出发:别只盯着语法和格式
低质量的代码审查往往视野狭窄,只关注语法、风格或细枝末节,而忽略了代码的可维护性和可扩展性。
高质量的审查,不仅看“改了什么”,更关注:
-
这些改动解决了什么问题?
-
是否会引发新的问题?
-
是否与系统整体架构协调?
我特别喜欢关注没有被改动的代码行,它们往往藏着真正的故事。比如,开发者常常忘记更新相关模块或文档,这可能引发 bug、破坏向后兼容性,甚至带来安全隐患。
审查时,我会问自己:
-
这段代码如何融入整个系统?
-
它与其他模块如何交互?
-
是否会影响未来的开发计划?
-
是否引入了更好的抽象?
这些问题,更多关乎系统设计,而非代码本身。忽视“大图景”,系统终将因不良变更而变得脆弱。
二、命名至关重要:它是代码的“门面”
我在审查中,花大量时间思考命名是否恰当。
命名虽难,却极其关键。好的命名是代码质量的基石,而糟糕的命名则是“代码异味”的信号,会成倍增加认知负担。
例如,以下代码:
fn update_player_stats(player: Player, bonus_points: i32, level_up: bool) -> Player {
let usr = player.username.trim().to_lowercase();
let updated_score = player.score + bonus_points;
let l = if level_up { player.level + 1 } else { player.level };
let l2 = if l > 100 { 100 } else { l };
Player { username: usr, score: updated_score, level: l2 }
}
变量名如 usr
、l2
含义模糊,逻辑难以追踪。
而优化后的版本:
fn update_player_stats(player: Player, bonus_points: i32, level_up: bool) -> Player {
let username = player.username.trim().to_lowercase();
let score = player.score + bonus_points;
let level = if level_up { player.level + 1 } else { player.level };
let level = if level > 100 { 100 } else { level };
Player { username, score, level }
}
每个变量名都清晰表达了其含义,最终变量名与结构体字段一致,极大提升了可读性。
三、敢于说“不”:拒绝低质量代码是种责任
我经常不得不拒绝某些提交,这从来不容易。毕竟,作者付出了努力,渴望被认可。
但不要为了让别人开心而妥协质量。要客观、清晰地解释原因,并提供更好的替代方案。拒绝的是代码,不是人。
在开源项目中,尤其需要“守门人”。如果每次都说“先合并以后再改”,技术债将迅速堆积,项目终将失控。
敢于说“不”,是资深工程师的担当。
四、代码审查是“人与人的沟通”
审查不仅是技术活动,更是团队协作与信任建设的过程。
我通常会尽量与作者面对面或结对审查前几次 PR,了解彼此的沟通风格,建立信任。
当沟通出现障碍时,不妨回到这种方式,人与人的理解,往往比代码本身更重要。
五、审查是“迭代”的过程,而非一次性通过
很多人希望“今天提 PR,明天就合并”。但高质量的代码往往需要多轮审查。
我的流程通常是:
-
第一轮:关注整体设计与架构
-
第二轮:深入细节与边界情况
-
如有必要,继续微调与优化
目标不是“快”,而是“对”。审查的意义,就在于共同打磨出高质量的代码。
六、做个“好人”:尊重与建设性胜过一切
即使意见不同,也要保持尊重与礼貌。避免说“你错了”,而是说“如果是我,可能会这样做”。
多使用“苏格拉底式提问”:
-
“这样会不会破坏现有流程?”
-
“你有没有考虑其他替代方案?”
-
“如果传入空数组,会发生什么?”
让人乐于接受你的反馈,是一种能力。
偶尔加上一句“这个想法很棒”,会让作者感受到被认可,积极反馈同样重要。
七、尽可能“跑一遍代码”
纸上得来终觉浅。我习惯把代码拉下来,跑一跑、测一测、甚至故意“搞破坏”。
尤其对 UI 改动、错误提示等用户可见变更,本地运行能发现很多静态代码审查遗漏的问题。
跑完后,我会回退改动,把发现的问题记录下来,作为评论提交。
八、主动沟通你的“可用性”
代码审查常成为开发瓶颈。如果你没时间审查,务必提前告知作者,避免对方空等。
我也在努力养成这个习惯:主动告知“我这几天很忙,可能无法及时 review”,这是对他人时间的尊重。
九、把审查当作学习的机会
每次审查,我都尝试学点新东西:新语法、新设计模式、新库,甚至别人解决问题的思路。
审查不是“浪费时间”,而是团队共同成长的最佳途径。
十、别做“格式警察”:把琐事交给工具
格式化、空格、换行,这些交给 linter 和 formatter 就好。
把精力留给真正重要的事:逻辑正确性、设计合理性、可维护性。
问问自己:“这会影响功能吗?会让未来的开发者困惑吗?”如果不会,就让它去吧。
十一、关注“为什么”,而非“怎么做”
审查时,多关注作者做出变更的动机与背景,而非单纯指出“你怎么这么写”。
一条好的评论,应该:
-
指出潜在问题
-
提供替代方案
-
解释原因与后果
-
给出文档或资料链接
这样的反馈,作者会感激你,也会真正成长。
十二、别怕问“蠢问题”
不懂就问,比假装懂要好得多。
你不懂的地方,别人也可能不懂。提问不仅能帮助你理解,也能促使作者反思自己的设计是否合理,甚至发现遗漏的文档或注释。
提出好问题,是一种超能力。
十三、定期征求对你“审查风格”的反馈
偶尔问问作者:
-
“我是否太苛刻或太琐碎?”
-
“我的反馈对你有帮助吗?”
-
“你觉得我哪些地方可以改进?”
让别人“审查你的审查”,是提升审查能力的捷径。
写在最后:好的审查,是一种艺术
代码审查,不只是技术活动,更是沟通、学习、成长与信任的过程。
它需要耐心、尊重、洞察力与责任感。
愿我们都能成为那种让人“期待”收到 feedback 的审查者。
翻译整理自 Matthias Endler 的博客文章《How To Review Code》,略有删减与调整,以便中文阅读。