审查清单设计:通用审查清单、语言特定清单、安全清单、性能清单的制定方法
说到审查清单,我见过太多团队把它搞成了「走过场」。一张清单列了上百条,评审时没人真看,最后成了摆设。
其实,好的审查清单不是越多越好。它应该像飞行员起飞前的检查单——每条都关键,漏一条就可能出事。
今天我就聊聊,怎么设计四类核心清单:通用的、语言特定的、安全的、性能的。
通用审查清单:所有代码都该过一遍
通用清单,说白了就是「不管什么语言、什么项目,都得查」的那些点。我习惯把它分成三块:
1. 逻辑与正确性
- 边界条件:数组越界、空指针、除零操作。我在项目中遇到过,一个简单的
list.get(list.size())就能让线上服务挂掉。 - 状态管理:状态机有没有遗漏的分支?并发场景下状态会不会乱?
- 异常处理:catch 了异常之后,是吞掉、重试还是抛出去?我见过有人 catch 之后只打了个 log,然后继续往下走——数据全错了。
2. 可读性与维护性
- 命名:变量名、函数名能不能「自解释」?别用
a、tmp、data这种。 - 函数长度:一个函数超过 50 行?嗯,该拆了。
- 注释:注释应该解释「为什么」,而不是「是什么」。代码本身就能说明「是什么」。
3. 测试覆盖
- 单元测试覆盖了核心逻辑吗?
- 有没有针对边界条件的测试用例?
- 异常路径有没有被测试到?
我的小技巧:通用清单别超过 15 条。太多的话,评审者会疲劳,反而容易漏掉真正重要的。
语言特定清单:不同语言,不同坑
每种语言都有自己的「脾气」。你想想看,Java 和 Go 的坑能一样吗?
Java 审查要点
- 空指针防御:所有外部输入、方法返回值,都要考虑 null 的情况。我习惯用
Optional或者Objects.requireNonNull。 - 并发安全:HashMap 在多线程下会死循环?是的,JDK 1.7 的 bug。现在虽然修了,但
ConcurrentHashMap才是正确选择。 - 资源释放:流、连接、锁,有没有用 try-with-resources?
Python 审查要点
- 类型提示:Python 3.6+ 支持类型注解了,别偷懒。我见过一个函数返回
list,结果里面混了字符串和数字,调用方直接崩了。 - 可变默认参数:
def foo(lst=[])是经典坑。默认列表会被所有调用共享。 - 异常捕获范围:别用
except:捕获所有异常。至少指定Exception,否则连KeyboardInterrupt都被吞了。
Go 审查要点
- 错误处理:Go 的错误处理是显式的。检查每个
err,别用_忽略。 - goroutine 泄漏:启动的 goroutine 有没有退出机制?用
select+context来控制生命周期。 - 指针 vs 值:大结构体用指针传递,小对象用值传递。别搞反了。
| 语言 | 常见缺陷 | 审查重点 |
|---|---|---|
| Java | 空指针、并发问题 | Optional、ConcurrentHashMap、try-with-resources |
| Python | 类型混乱、可变默认参数 | 类型注解、默认参数不可变、异常范围 |
| Go | goroutine 泄漏、错误忽略 | context 控制、err 检查、指针使用 |
安全清单:别让代码变成漏洞
安全审查,我建议从「攻击者视角」去想。如果我是坏人,我会怎么搞破坏?
输入验证
- SQL 注入:所有数据库查询用参数化查询,别拼字符串。我见过一个项目,直接把用户输入拼到 SQL 里——还好是内网系统。
- XSS 防御:输出到 HTML 的内容要转义。别相信前端传来的任何数据。
- 文件上传:检查文件类型、大小、路径。别让用户上传一个
.jsp文件然后直接执行。
认证与授权
- 密码存储:用 bcrypt 或 argon2,别用 MD5、SHA1。彩虹表了解一下?
- 会话管理:Token 有没有过期时间?有没有刷新机制?
- 权限校验:每个接口都校验权限,别只在前端隐藏按钮。
敏感信息
- 日志:别把密码、Token、身份证号打到日志里。
- 配置:数据库密码、API Key 别硬编码。用环境变量或配置中心。
- 错误信息:生产环境别返回详细堆栈。用户看到「NullPointerException at line 42」——这不等于告诉人家怎么攻击吗?
我曾经踩过的坑:有一次,我把 AWS 的 Secret Key 直接写在了代码里,还提交到了 GitHub。虽然几分钟后就发现了,但已经被人扫描到了。从那以后,我强制团队用 git hooks 检查敏感信息。
性能清单:别等线上出问题再优化
性能审查,我一般分「必查项」和「优化项」。必查项不过,代码不能上线。
必查项
- 循环中的数据库查询:N+1 问题。查一次拿到所有数据,别在循环里一条一条查。
- 大对象创建:频繁创建大对象会导致 GC 压力。考虑对象池或复用。
- 锁粒度:锁的范围越小越好。别把整个方法锁住,只锁关键代码块。
优化项
- 缓存使用:热点数据有没有加缓存?缓存过期策略合理吗?
- 批量操作:能批量就别一条一条处理。比如批量插入、批量更新。
- 异步处理:非核心流程(发邮件、写日志)能不能异步?别让用户等着。
一个真实案例:我审查过一个报表系统,每次查询要 30 秒。一看代码,里面有个三层嵌套循环,每层都查数据库。改成一次查询 + 内存计算后,耗时降到了 200 毫秒。你想想看,这差距有多大?
清单的维护与落地
清单不是写一次就完事了。我建议:
- 定期更新:每季度根据线上事故、新语言特性、新安全漏洞,更新清单。
- 团队共建:别一个人拍脑袋写。让团队一起讨论,大家认可了才会执行。
- 工具化:能自动检查的,就别靠人肉。比如 SonarQube、ESLint、Checkstyle 可以覆盖大部分通用和语言特定检查。
最后说一句:审查清单是「辅助」,不是「替代」。它帮你记住容易忘的点,但真正的代码质量,还是靠每个人的责任心。