审查沟通技巧
代码审查不仅是技术活动,更是团队沟通的重要环节。良好的沟通能够提升审查效率,维护团队氛围,促进知识共享。本章将介绍审查中的沟通技巧和最佳实践。
沟通原则
对事不对人
这是代码审查沟通的首要原则。审查意见应该针对代码本身,而不是编写代码的人。
不好的表达:
"你怎么又犯这种错误?" "你的代码逻辑太混乱了。" "这写得什么玩意儿?"
好的表达:
"这段逻辑在处理边界情况时可能有问题,建议添加对空值的检查。" "这个函数的职责不够单一,建议拆分成两个函数。" "这个实现方式可能有性能问题,建议考虑使用缓存。"
具体且可操作
模糊的评论会让开发者困惑,不知道该如何改进。审查意见应该具体说明问题所在,并提供可行的解决方案。
不好的表达:
"这里有问题。" "代码质量不高。" "需要优化。"
好的表达:
"这里的递归实现对于深层嵌套的数据可能会导致栈溢出。建议改为迭代实现:\n
java\npublic void processNode(Node root) {\n Stack<Node> stack = new Stack<>();\n stack.push(root);\n \n while (!stack.isEmpty()) {\n Node node = stack.pop();\n process(node);\n \n if (node.right != null) stack.push(node.right);\n if (node.left != null) stack.push(node.left);\n }\n}\n"
解释原因
说明为什么某个做法不好,以及改进后的好处,能够帮助开发者理解并记住这个知识点。
不好的表达:
"不要用魔法数字。"
好的表达:
"这里的
86400是一个魔法数字,表示一天的秒数。建议提取为命名常量,这样:\n1. 提高代码可读性,其他开发者一眼就能明白含义\n2. 便于维护,如果一天秒数定义改变,只需修改一处\n3. 避免重复,如果多处使用,可以复用常量\n\n建议修改为:\njava\nprivate static final int SECONDS_PER_DAY = 86400;\n\nint days = totalSeconds / SECONDS_PER_DAY;\n"
认可好的代码
代码审查不只是找问题,也应该认可和表扬好的实现。这能够激励开发者,营造积极的审查文化。
示例:
"这个错误处理设计得很好,使用了自定义异常并保留了原始异常信息,便于问题排查。"
"使用 Optional 来处理可能为空的情况是个很好的选择,避免了空指针异常。"
"这个函数拆分得很合理,每个函数职责单一,易于测试和维护。"
意见分级表达
阻塞性问题(Blocking)
必须修复才能合并的问题,通常涉及安全、功能缺陷或严重性能问题。
标记方式:
🔴 阻塞:这里存在 SQL 注入漏洞,必须使用参数化查询。
当前代码:
String query = "SELECT * FROM users WHERE id = " + userId;
建议修改为:
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?"
);
ps.setInt(1, userId);
使用场景:
- 安全漏洞
- 功能缺陷
- 严重性能问题
- 破坏向后兼容性
建议性问题(Suggestion)
建议采纳但不是必须的问题,通常涉及代码优化、设计改进等。
标记方式:
💡 建议:这里的循环可以替换为 Stream API,代码会更简洁。
当前实现:
List<String> result = new ArrayList<>();
for (User user : users) {
if (user.isActive()) {
result.add(user.getName());
}
}
建议改为:
List<String> result = users.stream()
.filter(User::isActive)
.map(User::getName)
.collect(Collectors.toList());
这样代码更简洁,也更容易理解。
使用场景:
- 代码优化建议
- 设计改进建议
- 更好的实现方式
- 性能优化建议
可选性问题(Nitpick)
仅供参考的小问题,通常涉及代码风格微调或个人偏好。
标记方式:
📝 可选:变量名 `data` 可以更具体一些,比如 `userList`,这样更容易理解变量的用途。
使用场景:
- 代码风格微调
- 个人偏好
- 非关键优化
- 命名建议
问题澄清(Question)
对代码逻辑或设计选择有疑问,需要开发者解释。
标记方式:
❓ 疑问:这里为什么选择使用乐观锁而不是悲观锁?这个场景下并发冲突的概率高吗?
使用场景:
- 理解设计意图
- 确认业务逻辑
- 了解技术选型原因
处理分歧
理解对方观点
当对审查意见有不同看法时,首先要确保理解对方的担忧。
示例对话:
审查者:这里的实现方式在高并发场景下可能会有性能问题。
开发者:我理解你的担忧。你是担心数据库连接池会被耗尽,对吗?
审查者:是的,特别是当外部服务响应慢的时候,可能会导致大量连接被占用。
解释你的考虑
说明自己选择当前方案的原因,帮助审查者理解上下文。
示例对话:
开发者:我选择同步调用是因为:
- 这个接口的调用频率不高(每天约 100 次)
- 业务上需要实时返回结果
- 外部服务的平均响应时间在 200ms 以内
不过你的担忧是有道理的,如果外部服务变慢确实会影响系统。
寻求共识
提出折中方案或下一步行动计划。
示例对话:
开发者:要不我们先按当前方案实现,同时:
- 添加超时控制(5 秒)
- 添加熔断机制
- 添加监控告警
- 记录一个技术债务,后续评估是否需要改为异步处理
审查者:可以,这样比较稳妥。记得在代码里加上 TODO 注释说明。
升级处理
如果双方无法达成一致,可以:
- 引入第三方:邀请技术负责人或其他资深开发者参与讨论
- 记录决策:将讨论结果和决策理由记录下来
- 优先合并:避免长期阻塞,可以合并后持续观察
回复技巧
作为代码作者
确认修复
已修复:在 commit abc123 中修改。
说明:采用了建议的方案,将循环改为 Stream API,代码确实更简洁了。
解释原因
关于这个问题,我考虑过使用缓存,但是:
1. 这个数据变化比较频繁
2. 查询本身很快(< 10ms)
3. 缓存会增加系统复杂度
所以我认为当前方案更合适。你觉得呢?
请求澄清
关于这个建议,我不是很理解。能否详细说明一下:
1. 具体是哪个地方有问题?
2. 推荐的做法是什么?
3. 有什么好处?
谢谢!
作为审查者
确认修复
修复看起来不错!Stream API 的使用很恰当。
一个小建议:可以考虑使用方法引用 `User::getName` 替代 lambda,代码会更简洁。
接受解释
明白了,你的考虑很有道理。在当前场景下确实不需要缓存。
建议添加一个注释说明这个决策,方便后续维护者理解。
坚持观点
我理解你的考虑,但是我仍然认为这个问题需要解决。
原因是:
1. 虽然当前调用频率不高,但业务在快速增长
2. 外部服务的不稳定性是我们无法控制的
3. 一旦出现问题,影响面会很大
建议我们先实现熔断机制,这样既能满足当前需求,又能防范风险。你觉得如何?
避免常见沟通陷阱
陷阱一:过于主观的评论
不好的表达:
"我不喜欢这种写法。" "这样写不好看。"
好的表达:
"这种写法在团队协作中可能会引起混淆,因为变量名
data不够具体,建议使用更具描述性的名称,如userList或activeUsers。