代码评审指南






4.84/5 (34投票s)
代码评审是对计算机源代码进行的系统性检查(通常称为同行评审)。
什么是代码评审?
代码评审是对计算机源代码进行的系统性检查(通常称为同行评审)。它的目的是在初始开发阶段发现并修复被忽略的错误,从而提高软件的整体质量和开发人员的技能。
为什么评审很重要?
.. 仅靠软件测试的有效性有限——单元测试的平均缺陷检测率仅为 25%,功能测试为 35%,集成测试为 45%。相比之下,设计和代码评审的平均有效性分别为 55% 和 60%。评审结果的案例研究令人印象深刻:
- 在一个软件维护组织中,在引入代码评审之前,55% 的单行维护更改存在错误。引入评审后,只有 2% 的更改存在错误。考虑到所有更改,引入评审后,95% 的更改首次就能成功。在引入评审之前,首次成功的不到 20%。
- 在一个由同一组人开发的 11 个程序中,前 5 个程序是在没有评审的情况下开发的。其余 6 个程序是在有评审的情况下开发的。所有程序发布到生产环境后,前 5 个程序平均每 100 行代码有 4.5 个错误。经过评审的 6 个程序平均每 100 行代码只有 0.82 个错误。评审将错误减少了 80% 以上。
- Aetna 保险公司通过使用评审发现了程序中 82% 的错误,并将开发资源减少了 20%。
- IBM 的 50 万行 Orbit 项目使用了 11 个级别的评审。该项目提前交付,并且只有正常预期错误的约 1%。
- 一项针对 AT&T 拥有 200 多人的组织的研究报告称,该组织引入评审后,生产力提高了 14%,缺陷减少了 90%。
- 喷气推进实验室估计,通过早期发现和修复缺陷,每次评审可节省约 25,000 美元。
代码评审的主要目标是:
- 在流程早期发现并修复缺陷。
- 团队成员互相学习,从而更好地共享代码库的理解。
- 有助于在设计和实现上保持一致性。
- 有助于识别团队中的常见缺陷,从而减少返工。
- 增强利益相关者对执行的技术质量的信心。
- 统一的理解将有助于在任何团队成员缺席时进行人员互换。
- 不同的视角。“另一双眼睛”增加了客观性。这与分离编码和测试团队的原因类似,同行评审提供了识别问题所需的距离。
- 自豪感/奖励。对许多程序员来说,认可编程才华是一种重要的奖励。
- 团队凝聚力。一起工作有助于拉近团队成员之间的距离。它也为编码经常带来的孤独感提供了一个短暂的喘息机会。
评审者关注的主要领域如下:
- 通用单元测试
- 注释和编码约定
- 错误处理
- 资源泄漏
- 线程安全
- 控制结构
- 性能
- 功能
- 安全
角色和职责
- 开发者: 是编写待评审代码并发起评审请求的人。
- 评审者: 是将要评审代码并向开发者报告发现问题的人。
给开发者的建议
- 主要评审者是作者,即您。
- 为自己创建一个清单,列出代码评审通常关注的事项。这个清单中的一些项目应该很容易制定。它应该遵循编码标准文档的结构。因为这是您自己的清单,所以您可以专注于自己遇到的问题,而跳过您很少遇到的问题。使用清单检查您的代码并修复发现的任何问题。这不仅可以减少团队发现的问题数量,还可以缩短代码评审会议的完成时间——每个人都会乐于在评审上花费更少的时间。
- 您不是您的代码。 请记住,评审的重点是发现问题,而问题是会被发现的。当问题被揭示出来时,不要把它当作人身攻击。
- 理解并接受您会犯错。 重点在于及早发现错误,以免其进入生产环境。幸运的是,在我们这个行业中,除了少数为 JPL 开发火箭制导软件的人之外,错误很少是致命的,所以我们可以,也应该,学习、一笑而过,然后继续前进。
- 无论您有多么“懂功夫”,总会有人比您更懂。 这样的人可以通过询问教您一些新技巧。寻求并接受他人的意见,尤其是当您认为不需要的时候。
- 不要在未咨询的情况下重写代码。 “修复代码”和“重写代码”之间有一条微妙的界限。了解区别,并在代码评审的框架内进行风格上的更改,而不是独自一人强行执行。
- 世界上唯一不变的是变化。 对它持开放态度,并微笑着接受它。将需求、平台或工具的每次更改视为新的挑战,而不是需要对抗的严重不便。
- 为您的信念而战,但优雅地接受失败。 要明白,有时您的想法会被推翻。即使您最终被证明是正确的,也不要报复,最多说几次“我告诉过你了”,并且不要让你珍贵的想法变成牺牲品或号召。
- 不要成为“房间里的那个男人”。 不要成为那个在黑暗的办公室里编码,只出来买可乐的人。房间里的那个人脱节、隐身、失控,在开放、协作的环境中没有立足之地。
- 请注意,评审会议不是问题解决会议。
- 帮助维护编码标准。 对于讨论中未包含在编码标准中的内容,可以主动添加到编码标准中。开发人员在代码评审实践具有对抗性的组织中面临的挑战之一是,他们经常不知道下一个问题会来自哪里。如果您将每个问题记录到编码标准中,下次进行代码评审时就可以用清单进行检查。这也有助于巩固概念,使其更不容易错过使用反馈的机会。
给评审者的建议
- 批评代码而不是人——善待编码者,而不是善待代码。 尽可能使您的所有评论都积极向上,并以改进代码为导向。将评论与本地标准、程序规范、性能提升等联系起来。
- 尊重、礼貌和耐心地对待那些不如您懂的人。 经常与开发人员打交道的非技术人员几乎普遍认为,我们充其量是骄傲自大,最坏是爱哭鬼。不要用愤怒和不耐烦来强化这种刻板印象。
- 真正的权威只源于知识,而非职位。 知识产生权威,权威产生尊重——所以,如果您想在一个没有自负的环境中获得尊重,就培养知识。
- 请注意,评审会议不是问题解决会议。
- 提问而不是陈述。 陈述是带有指责意味的。“你没有遵循这里的标准”是一种攻击——无论是故意的还是无意的。问题“你当时采用这个方法是出于什么考虑?”是在寻求更多信息。显然,这个问题不能用讽刺或居高临下的语气说出来;但如果做得恰当,它常常能让开发人员说出自己的想法,然后询问是否有更好的方法。
- 避免问“为什么”。 尽管有时非常困难,但避免问“为什么”可以大大改善气氛。就像陈述带有指责意味一样,“为什么”的问题也是如此。大多数“为什么”的问题都可以改写成不包含“为什么”的问题,其效果可能非常显著。例如,“你为什么不遵循这里的标准……”与“你这里偏离标准的原因是什么……”
- 记住要赞扬。 代码评审的目的不仅仅是告诉开发人员如何改进,也不一定是他们做得很好。人的天性是我们希望并需要因成功而得到认可,而不仅仅是指出缺点。因为开发本质上是一项开发者倾注心血的创造性工作,它常常能触及他们的内心。这使得赞扬的需求更加关键。
- 确保您有良好的编码标准可供参考。 代码评审以组织的编码标准为基础。编码标准应该是开发人员之间就生成高质量、可维护代码达成的共识。如果您正在讨论的内容不在您的编码标准中,那么您需要做一些工作才能将其纳入编码标准。您应该定期问自己,讨论的内容是否在您的编码标准中。
- 请记住,解决问题往往有不止一种方法。 尽管开发者可能以不同于您的方式编写代码,但这不一定就是错误的。目标是高质量、可维护的代码。如果它达到了这些目标并遵循了编码标准,那么这就是您所能要求的。
- 您不应该匆忙完成代码评审——但同时,您需要及时完成。 您的同事正在等您。
- 一次评审的代码行数少于 200-400 行。
安全代码评审
如果应用程序需要非常关注安全性,那么 www.owasp.org 是获取资源的最佳去处。以下是一份非常好的安全代码评审文档可供参考:https://www.owasp.org/images/2/2e/OWASP_Code_Review_Guide-V1_1.pdf
为评审发现指定严重性
代码问题的严重性应如下。评审者必须首先关注高严重性问题,然后是中等严重性问题,最后是低严重性问题。
- 命名约定和编码风格 = 低
- 控制结构和逻辑问题 = 中或高
- 冗余代码 = 高
- 性能问题 = 高
- 安全问题 = 高
- 可伸缩性问题 = 高
- 功能性问题 = 高
- 错误处理 = 高
- 可重用性 = 中
开发人员和评审者的清单
清单是强烈推荐的方法,用于查找您忘记做的事情,并且对作者和评审者都有用。遗漏是最难发现的缺陷——毕竟,很难评审不存在的东西。清单是解决此问题的最佳方法,因为它会提醒评审者或作者花时间检查可能缺失的内容。清单会提醒作者和评审者确认所有错误都已处理,函数参数已针对无效值进行了测试,并且已创建了单元测试。
另一个有用的概念是个人清单。每个人通常都会犯同样的 15-20 个错误。如果您注意到自己典型的错误是什么,就可以制定自己的个人清单(PSP、SEI 和 CMMI 也推荐这样做)。评审者将负责确定您的常见错误。您要做的就是保留一份简短的清单,列出您工作中常见的缺陷,尤其是您经常忘记做的事情。一旦您开始在清单中记录您的缺陷,您就会开始犯更少的错误。规则会在您脑海中清晰起来,您的错误率就会下降。我们一次又一次地看到这种情况发生。
开发人员清单
描述 |
已确认? |
我的代码已编译 | |
我的代码已经过开发者测试,并包含单元测试 | |
我的代码在适当的地方包含 javadoc | |
我的代码是整洁的(缩进、行长、无注释掉的代码、无拼写错误等) | |
我已考虑异常的正确使用 | |
我已适当使用日志记录 | |
我已删除未使用的导入 | |
我已删除 Eclipse 警告 | |
我已考虑可能的 NPEs | |
代码遵循编码标准 | |
代码中是否有任何残留的存根或测试例程? | |
代码中是否仍有任何硬编码的、仅用于开发的设置? | |
是否考虑了性能? | |
是否考虑了安全性? | |
代码是否释放资源?(HTTP 连接、数据库连接、文件等) | |
边界情况是否已妥善记录,或是否有已知的框架限制的解决方法? | |
是否可以将任何代码替换为对外部可重用组件或库函数的调用? | |
线程安全性和可能的死锁 |
评审者清单
描述 |
已确认? |
注释易于理解,并对代码的可维护性有所贡献 | |
注释不多也不冗长 | |
类型已尽可能泛化 | |
已适当使用参数化类型 | |
已适当使用异常 | |
重复的代码已被提取 | |
已适当使用框架——方法都已适当定义 | |
命令类被设计为只执行一项任务 | |
JSP 不包含业务逻辑 | |
单元测试存在且正确 | |
已检查常见错误 | |
已尽可能消除潜在的线程问题 | |
任何安全隐患都已得到解决 | |
已考虑性能 | |
功能符合当前的设计/架构 | |
代码是可单元测试的 | |
代码不使用不合理的静态方法/块 | |
代码符合编码标准 | |
日志记录使用恰当(正确的日志级别和详细程度) | |
NPEs 和 AIOBs |
我还将它们制成了可打印的清单格式:CodeReview_ChecklistforDevelopers,CodeReview_ChecklistforReviewers。
参考文献
- http://www.codinghorror.com/blog/2006/05/the-ten-commandments-of-egoless-programming.html
- http://www.developer.com/java/other/article.php/3579756
- http://www.smartbear.com/docs/BestPracticesForPeerCodeReview.pdf
- http://www.amazon.com/Peer-Reviews-Software-Practical-Guide/dp/0201734850/ref=la_B001IGNXQS_1_3?ie=UTF8&qid=1354616082&sr=1-3
- http://www.techrepublic.com/article/developers-guide-to-peer-reviews/1050834
- http://smartbear.com/SmartBear/media/pdfs/best-kept-secrets-of-peer-code-review.pdf