为什么要进行代码评审?
作者 | Kaiden 爱数 AnyBackup 数据库保护研发部-研发总监
什么是技术债务?
Ward Cunningham(沃德·坎宁安)在 1992 年首次提出:交付第一次代码就像陷入债务。 债务是可以加快开发速度,只有通过重写代码,及时偿还债务。如果不偿还债务,就会发生危险。 把时间花在写一些不正确的代码上的每一分钟都算作该债务的利息。 整个软件项目可能在未合并代码的部署,面向对象设计或其他方面的债务问题而陷入停顿。
同时,卡内基-梅隆大学软件工程研究所(SEI)的 Robert Nord 在《The Future of Managing Technical Debt》提出了“技术债务全景图”(Tech Debt Landscape)的概念。
技术债务全景图主要从两个方向来分析技术债对于软件系统的影响:可维护性(Maintainability)、可演进性(Evolvability),同时结合问题的可见性(Visibility)分析技术债务对于软件开发过程的影响。
2011 年 3 月 25 日 Gartner 发布 Gartner-CAST 白皮书:货币化技术债务,有如下描述:
技术债务的表象和消除手段
基于以上的信息,相信大家会对技术债务有足够的的认识,那么我们思考以下几个问题:技术债务的载体是什么?技术债务具体的体现是什么?
程序的最初原型是代码,那么技术债务的载体也应该是代码。对于代码中的一些坏味道,就是我们所说的债务,具体体现在以下几个方面:
1、 不能均匀分布的复杂性。
2、 重复代码。
3、 不合适的注释。
4、 违反代码规范。
5、 没有单元测试。
6、 缺陷和潜在的缺陷。
7、 设计和系统架构受限与当时的条件。
这些点带来的问题也就显而易见了,会导致:额外的研发成本;不稳定的产品质量;难以维护的产品。
那么如何在一个项目中解决这些问题,偿还债务,我们通常会建议开发人员优先解决其中的一些简单的问题,比如重复的代码等,一般的处理顺序如下:
1、 大量重复的代码。
2、 类之间的耦合。
3、 拆分复杂的方法
4、 条件判断嵌套过多
5、 缺少必要的异常处理
6、 多表关联和缺少索引
7、 代码风格和缺陷
8、 安全漏洞
在消除技术债务时,我们通常建议开发人员要注意以下基本准则:
1、 让技术债务呈良性下降趋势
2、 优先解决高频修改的部分
3、 在新项目中启动试点
4、 技术债务无法被消灭,也不能等到太晚
当然在大型的软件项目开发过程中,往往是一个团队或者多个团队来共同研发,这就涉及到团队配合和流程规范上的问题了,我们通常建议采用如下的几个准则指导我们解决问题,这几点在后续的章节也会有更加详细的阐述:
1、 达成共识
2、 可见
3、 止损
4、 改善
引入代码评审机制
当我们面对技术债务时,我们在实践过程中发现,代码评审是一种消除技术债务极为有效的方法。它可以为我们带来很多的好处,例如:传承高水平的代码编写技能;纠正人员编码态度;识别架构风险;提升研发效率和质量等等。
然而在项目管理的过程中,如何落地代码评审呢?在前期实践的过程中,我们采取的是发现问题,解决问题的思路,确定了以下几个环节(以下的点和具体的业务具有相关性,大家可参考其中的点提炼各自团队需要关注的核心点进行评审):
评审对象
新增的功能模块
里程碑测试数据异常模块
修改引入超过 5 个的模块
BUG 数超过 15 的模块
BUG Reopen 率超过 2%的模块
评审维度
代码结构
代码的目录层次结构
功能组件划分:组件间调用关系(不能环状调用,层次深度)
功能类划分
UT 覆盖
UT 设计合理性,UT 覆盖率
代码复杂度
代码的圈复杂度
代码的函数拆分、可读性、注释等
核心功能满足程度
关键的功能点设计是否合理
是否和顶层架构设计相符
功能效率
代码的安全规范
敏感信息是否加密
是否存在内存泄露(工具扫描)
是否符合安全规范,安全漏洞扫描,无安全漏洞
编译告警整改,无编译告警
异常处理
是否进行了异常处理
是否重复包装
是否吞并异常
异常日志是否记录完整等
代码提交规范
是否符合代码的合并规则:合并请求是否能够清晰的描述代码的变更
提交粒度是否合理:合并的粒度是否存在大规模积压代码一次提交现象,95%的提交小于 10 个文件,100 行代码。
架构设计
可扩展性
可测试性
评审流程
评审模块前期准备
完成代码规范的自检,保证代码不会出现明显的代码规范错误。
备份模块的 GNS 设计,给出示例。
备份元数据的设计,给出 thrift 的结构定义。
核心功能代码实现逻辑,出具时序图。
代码的圈复杂度数据,由构建系统提供。
模块的 UT 覆盖率数据,由构建系统提供。
参审人员前期准备
参审人员为小组的组长,代码审核人员,团队骨干等。
必须提前阅读代码,熟悉代码的结构和实现逻辑。
列出代码设计的优点和改进项。
评审过程
评审方式:按照评审维度,参评人员进行展示,评审人员进行评估,给出评价,如果需要改进,引导参评人员识别改进点并给出改进建议。
评审时间:时间控制在 30~45 分钟。
评审人数:单次评审,一般不超过 4 位评审官。
改进计划
评审结果
评审组成员根据评审维度给出评审结果,并进行记录,后期划分为优秀设计,代码优化,功能优化等。
优秀设计
优秀的设计由团队负责人,以邮件的形式向团队推广,并邀请作者进行分享。
代码优化
代码优化由模块负责人设计改进计划和改进方案,提交小组长进行评估,并排期落地。
功能优化
功能优化由小组长录入优化型需求,并设计功能目标和质量目标,进行排期落地。
公司的规范是我们进行代码的评审的最低准入条件,我们在评审中要求,代码必须自检,必须符合公司基础的代码规范要求,才能进入评审环节,公司制定的各种规范指导大家进行开发,如:
· 《代码规范》
· 《静态代码检查规范》
· 《微服务开发规范》
· 《数据库设计规范》
· 《程序日志规范》
· 《错误码规范》
· 《单元测试指南》
· 《开源软件使用规范》
· 《安全设计与开发规范》
· 《接口设计规范》
同时各个子系统也针对各自的模块设计的顶层的架构设计,各自模块的架构设计必须和顶层的架构设计保持一致,使得项目管理,开发流程,软件设计上做到尽可能的统一,从而避免各自为政,野蛮生长的情况。例如数据库保护子系统的顶层架构设计如下:
当然公司也针对代码评审制定了详细的代码评审规范,目前规范已经迭代到 3.0 版本,由最初的各个团队提交针对于重大价值特性的代码的月度代码评审报告,到后续的全公司季度重大价值特性抽审,以及新增的月度初提交级研发人员代码成长记录档案,来跟踪促进初级开发人员的成长,代码评审的规范越来越全面,合理。
例如针对重大价值评审,规范详细列举了评审的各个大项以及其中包含的评审维度。其中就包含了前端评审细则,后端评审细则,以及架构设计评审细则。架构评审中就包含众多的维度,如:
1、 架构设计是否满足业务需求及符合行业技术现状。
2、 是否有架构设计视图,并能详细展示全局的架构设计。
3、 是否符合架构设计的基本原则。
4、 是否满足功能特性要求。
5、 是否满足非功能特性要求。
6、 架构目标满足程度,关键技术是否经过验证。
7、 架构先进性,架构设计中是否具备设计亮点。
对于代码评审的思考
代码评审是一项持续性工作
在我们平常的研发过程中,代码评审可以是一件事,也可以是一个阶段的事,或者是一件需要持续坚持做的事,当我们以不同的态度对待它,就会产生不同的效果。我始终相信,只有持续的发现问题(技术债务),发起评审,制定改进计划,落地改进计划,才能真正的让代码评审发挥应有的效果,让技术债务呈良性下降趋势,才能做到债务的可见(暴露风险和潜在风险),止损(及时消灭潜在风险),改善(提升管理效率和研发质量)。
关注问题转为关注能力—提升团队的可持续性
当一个团队持续做一件事时,将它常态化,那么它就会形成一种流程规范,并最终固化为人员的意识,而不是在代码评审是才会考虑消除技术债务,而是在日常研发中,他就会以评审的要求来审视自己的代码,从而在源头上杜绝可能发生的潜在风险,这样,就会在团队中达成一种无形的共识,我们在研发迭代过程中就有义务及时发现代码的坏味道,并修复它。
在这样的一个组织中,它就是一种可固化的组织能力,当新的成员进入时这个团队时,也会快速的对于这种共识产生认知,也会从前辈那里继承同样的能力。当然,这会是一个长期的过程,而这个过程中,我们更应该关注团队的赋能和人员的能力培养,而不是只关注如何解决问题。
版权声明: 本文为 InfoQ 作者【爱数技术范儿】的原创文章。
原文链接:【http://xie.infoq.cn/article/b4f34e42f0f0dd722057d8c62】。文章转载请联系作者。
评论