代码审查技巧:审查清单、常见问题、如何给出建设性反馈
代码审查这件事,我做了十几年。说实话,刚带团队那会儿,我也觉得审查就是找bug。后来才明白,好的审查能帮团队少走太多弯路。今天我把自己的经验梳理一下,希望能给你一些启发。
一、审查清单:我每次必查的几项
我个人习惯把审查清单分成三个层次。不是死记硬背,而是形成肌肉记忆。
1. 逻辑正确性
这是底线。代码能不能跑?边界条件处理了吗?
- 空指针、数组越界、除零操作——这些是C语言的经典坑
- 循环终止条件是否正确?我见过有人写
for(i=0; i<=n; i++)结果数组只有 n 个元素 - 资源释放路径是否完整?malloc 和 free 要成对出现
核心原则:先保证逻辑正确,再谈代码优雅。逻辑错了,风格再好也没用。
2. 内存与资源管理
C语言里,内存泄漏是慢性病。我曾在项目中排查过一个泄漏问题,查了整整两天,最后发现是一个异常路径没释放。
- 每个 malloc/calloc/realloc 都有对应的 free 吗?
- 文件描述符、socket、锁——这些资源是否都正确释放?
- 结构体中的指针成员,释放时有没有先释放子成员?
避坑指南:我曾经在嵌入式项目中,因为忘记释放一个临时缓冲区,导致系统运行72小时后内存耗尽重启。从那以后,我审查代码时第一件事就是画资源生命周期图。
3. 代码可读性与维护性
你想想看,一段代码写出来,可能要被读几十次。可读性差就是给自己挖坑。
- 变量命名是否清晰?
int a;不如int timeout_ms; - 函数是否太长?我建议一个函数不超过50行
- 注释是否在解释「为什么」而不是「是什么」?
二、常见问题:我踩过的坑
这些年审查过的代码,问题类型其实很集中。我总结了几类高频问题。
| 问题类型 | 典型表现 | 严重程度 |
|---|---|---|
| 未初始化变量 | 局部变量不赋初值就直接使用 | 高 |
| 缓冲区溢出 | strcpy、sprintf 不检查长度 | 极高 |
| 类型转换不当 | 有符号与无符号混用,隐式截断 | 中 |
| 宏定义陷阱 | 宏参数不加括号,导致运算优先级错误 | 高 |
| 死代码 | 永远不会执行到的分支或赋值 | 低 |
举个例子,宏定义的问题我见过太多次了:
// 错误写法
#define SQUARE(x) x * x
// 调用 SQUARE(a+1) 展开为 a+1*a+1,结果完全不对
// 正确写法
#define SQUARE(x) ((x) * (x))
嗯,这里要注意,宏的每个参数和整个表达式都要加括号。这是基本功,但很多人会忘。
三、如何给出建设性反馈
审查代码不是批斗会。我见过一些审查,搞得像在审犯人,效果反而不好。怎么给反馈?我分享几个心得。
1. 用提问代替指责
别说「你这里写错了」,换成「这里我有点疑惑,能解释一下你的思路吗?」
- 「这个分支条件,是不是考虑一下边界情况?」
- 「这个函数名,我第一眼没看出它的作用,是不是可以改得更明确?」
2. 给出具体建议
光说「这段代码不好」没用。要给出改进方向。
我的习惯:每次提问题,至少附带一个改进方案。哪怕只是「建议把这段逻辑提取成单独函数」,也比只说「太乱了」强。
3. 区分「必须改」和「建议改」
不是所有问题都要改。我一般分三个等级:
- 阻塞性:逻辑错误、安全漏洞——必须改
- 建议性:可读性、性能优化——建议改
- 风格性:缩进、空格——可改可不改
为什么要区分?因为如果每个问题都要求改,对方会疲劳,反而忽略真正重要的问题。
4. 先肯定,再指出问题
代码审查也是团队协作。先认可对方的努力,再提改进意见。
- 「这个模块的整体架构设计得很好,特别是错误处理部分很清晰。不过有个小地方,你看这里的内存释放路径是不是漏了?」
- 「你写的这个算法效率很高,我学到了。只是命名上,是不是可以更直观一些?」
四、审查流程与知识体系
下面这张图是我自己总结的审查流程,你可以参考一下。
五、审查中的沟通技巧
最后聊点软技能。代码审查本质上是沟通,沟通不好,技术再强也没用。
- 当面沟通优于文字:复杂问题,我建议直接走到对方工位聊。文字容易产生误解。
- 控制审查节奏:一次审查不要超过400行代码。超过这个量,人的注意力会下降。
- 建立团队审查文化:让每个人都参与审查,而不是只有资深工程师审新人。互相学习才是最好的成长。
我的经验:好的代码审查,能让团队的整体代码质量提升一个档次。它不只是找bug,更是知识传递和标准统一的过程。
说白了,代码审查这件事,做得好是团队加速器,做不好就是内耗。希望今天分享的这些,能帮你把审查这件事做得更顺手。