Code Review的目的除了提高代码质量,提前发现bug外,还包括统一团队的代码规范,比如经常会碰到有人说你这个变量命名不对,或者这里缩进不应该用tab,甚至这里应当多加一个空白行。而类似架构或者设计模式这样的“大”问题,我个人觉得并不适合在code review的时候去讨论。如果这方面有问题,那说明之前design review没有做好或者有可能根本没有做design review。
具体的规则可能每个部门各不相同,比如有的部门给每个组件规定几个owner,改到那块代码必须找至少一个owner做review。有的部门还规定每次code review至少要有一个senior级别以上的码农参与,等等。
从工具上来说,现在的码农还是比较幸福的了。n年以前做code review,就是自己做个bbpack发到邮件上,其实就是一个diff,reviewer就看着windiff,有意见的就拿个小本本记下来:某某行号,某某问题,然后email沟通。
方法有多种,目前最被认可或运用的方法莫过于CodeReview活动了。
那么 CodeReview到底能给团队带来什么?什么样的团队需要进行CodeReview活动?如何有效开展CodeReview活动?用哪种方式会比较好呢?
笔者为了接地气地研究这个实践,特选择了“手机管家高权限应用组”作为试点团队进行活动开展,这是一个对CodeReview活动非常认同并且愿意持续改进的团队,经过一年的运作,该团队CodeReview活动运作成效显著。
接下来笔者就根据试点经验,总结一下对CodeReview这个实践的看法和思考,希望能对想要或正在进行CodeReview活动的团队提供借鉴作用。
一、CodeReview到底能给团队带来什么?
通过参与实战和团队成员讨论思考,我们认为CodeReview最终的作用将归到促进工程师日常代码交流和人员的成长上面来,与此同时作为辅助手段来对产品质量进行把关。
但一般来说,很多团队在CodeReview前期重点会是找问题(代码规范、潜在缺陷、BUG,代码设计等等),而后期随着问题的逐渐减少和习惯的逐步养成,工程师交流文化的营造将转化成重点,中期当有大批新人加入时,问题找茬将又上升为重点,如此复始。
总结一下,大多数情况下,找问题会是CodeReview活动启动的初衷,但越到后期它更大的意义将演变成工程师交流土壤的培育和人员成长的促进。
二、什么样的团队需要进行CodeReview活动?
CodeReview作为业界公认的最佳实践,如果每个团队都能运用起来,固然是最好的,但是由于这项活动跟“人”这个因素密切挂钩,所以,它是否能有效运作跟团队状态、技术信仰和领导者诉求等都有莫大关系。今天,笔者想分享下个人在“到底什么样的团队需要”和“什么样的团队暂时不适合”这方面的思考。这里欢迎大家更多的交流讨论。
从代码质量提升的角度上看,以下类型的团队,笔者建议把CodeReview活动有效运作起来:
技术驱动型团队:一般涉及系统底层逻辑较多,功能路径难以被测试覆盖,而产品质量问题很多时候是致命的,所以这样的团队更多需要开发编码的严谨性和相关代码质量的保证活动。手机管家高权限应用组就属于这一类型。
公共服务型团队:一般服务于多个团队,一旦出现质量问题影响范围会比较广,所以除了在测试方面加以把关外,通过CodeReview活动来提升开发质量是非常有必要的。
测试缺失型团队:这样的团队由于缺乏测试环节,质量问题带到线上的风险会很高,强烈建议在开发环节做好自检工作。
新人密集型团队:新人的代码可读性往往是比较差的,特别需要组织能及时给予纠正,帮助新人养成良好的编码习惯。同时如果团队产出的代码可读性较高时,新人也可以更快上手工作。
任何有主观意愿的团队:这样的团队或领导者认同CodeReview的意义,或团队成员对代码质量提升有追求。
CodeReview活动跟人这个因素密切相关,从其带有的这个主观特点来说,笔者认为以下类型的团队暂时不适合开展CodeReview活动:
不认同型团队:即领导和团队骨干都不认同CodeReview意义的团队,这样的团队无论从推动还是坚持上都有很大挑战。
疲于应付型团队:这种团队一般没有建立必要的持续提升机制,每天淹没在各种需求沟通实现变更和优化中,自然,代码质量提升活动也很难被列入backlog。
创新型团队:这种团队的重要任务是要把产品快速推向市场进行价值验证,所以在代码编写上要求足够敏捷,代码暂时的混乱完全可以接受。
综上,笔者建议大家在考虑自身团队是否要推行CodeReview时,可结合团队实际状态进行综合考虑。但一般来说,如团队主观意愿没有问题,就可以大胆推行开展。
三、如何有效开展CodeReview活动?
要想在团队内部有效运作CodeReview活动,必备四要素(如下图)。如果您的团队没有把CodeReview活动有效开展下去或者正深受烦恼,这部分内容希望您仔细看看。
1、代码规范:明确Coding规则
如果一开始不定义好团队Coding标准,那在检视过程中就会存在两种情况:一种是各种不同的意见很难快速达成一致,影响review效率,另外一种是团队根本就不会重视代码规范的检视, 如果是前者还好,毕竟大家都还在关注什么写法是好的或对的这个问题,只要中途愿意建立起Coding规则,问题就能很快解决。而笔者跟进过的一个团队恰恰就出现了后者的情况:该团队由于前期没有明确Coding规则, 过程中大部分开发人员对规范类问题直接无视,CodeReview运作一段时间后代码中依然存在命名不规范,可读性较差等问题,直接影响了活动效果,这是我们非常不愿意看到的。
当然也有团队负责人说了,每天纠结于空格少了,行数字符多了等细节问题没意义啊,不想浪费这个时间,因此我们不需要代码规范。我个人不认同这个观点,因为代码规范并不只包括空格和字符等约束纬度,还包括了注释的要求,命名的规范,命名是否词能达意,代码结构安排等等影响代码可读性的因素, 如若这些方面连基本规则都没有,那一定会出现之前说的那两种情况(争议太多 or 完全忽视),效果可想而知。所以你可以根据自己的看法或需求做一定的规则定制,但不能没有Coding规则。
2、检视指南:消除困惑和迷茫
检视指南又名CodeReview-checklist。一个团队并不是所有人都是老司机,有很多同学是没有代码review经验的,他们往往不知道应该重点 check哪些点。
这个时候结合自身业务特点和团队之前踩过的坑,制定一个checklist是非常必要的:
什么写法可能导致性能低下?
哪个接口要慎用?
哪些设计方式需要规避?
什么习惯容易引发内存泄漏?
等等。。。
这样可以让经验不足者在不知道要review什么时,能有的放矢,过程中逐步积累起经验。
以下是一个团队建立起检视指南前后,CodeReview发现问题数的变化,足见建立检视指南的重要性。
当然也有人说,我的团队代码检视都是让资深骨干做的,不存在不知道怎么review的情况。
但是我想说,骨干员工的时间毕竟很宝贵,他们也往往很忙碌,为什么不让更多的成员一起来参与review工作呢,毕竟CodeReview不仅仅是找茬,也是代码的交流和学习!
3、总结优化:透明问题,持续优化(非常重要)
我们看到很多团队的CodeReview活动坚持不下来或逐步流于形式,其实最主要原因是过程中缺乏定期回顾和总结,从而不知道如何有效促进和帮助团队更好运作。
为了更好地促进这项活动,手机管家高权限应用组就专门成立了CodeReview组委会,这个组织每月都会对CodeReview运作状况进行总结,分析问题,解决问题,持续优化,其最后的效果能在团队内外均获得较高的认同度,可以说总结优化这个环节起到了非常关键的作用。
4、激励机制:激发主观能动性
由于CodeReview本身跟人的经验或者意识都有很大关系,很多时候我们会为调动不起开发同学的积极性而烦恼,所以为了让大家更好的参与这个活动,我们一般都需要制定相应的激励机制。也有人说了,如果是leader强制跟考核挂钩,那就不需要这个东东了,嗯,但换个角度看,跟考核挂钩难道不是另外一种“激励”方式吗?哈哈。。。
激励机制的设立有很多种,一般来说,都是在定期回顾的基础上根据CodeReview的实际情况对表现积极的同学进行一定的礼品奖励(选择什么礼品,要看组织的经济状况,哈哈)。
笔者跟进的团队每月会从CodeReview提交次数和发现问题数等纬度进行质量之星选举,礼品包括了书籍,公仔,徽章等等,效果不错,做法供大家参考。
总之,代码规范、检视指南、总结优化和激励机制这四个因素对成功运作CodeReview活动都非常关键,但每一项里面的内容具体要如何定义,团队在参考业界做法的基础上可根据实际情况进行一定的定制。
四、CodeReview方式很多,用哪种会比较好呢?
目前业界运作CodeReview的方式有多种方式:强制&非强制、线上交流&线下会议、小片段&大模块、事前&事后、高频率&低频率,等等……据了解,目前每种形态都有各自的市场,被不同的团队运用着。
接下来笔者个人角度分析下各种形态的优缺点,供大家参考:
强制&非强制: 按照经验,CodeReview启动前期建议采用强制要求,否则很难有效开展起来。坚持一段时间待习惯养成后再考虑自由度。
小片段&大模块:如果想要让问题暴露更充分或降低review的难度,建议采用细粒度方式进行,即小片段提交小片段review。如果更关注全局设计和逻辑思路的学习和找茬,那么可以用模块方式统一review。但很多时候这两种方式是可以结合运作的。
线上交流&线下会议: 如果想提高效率,建议采用线上方式进行交流,这里要推荐公司的Code平台,上面支持CodeReview的功能都已经比较齐全。如果更喜欢全员一起找茬的那种快感,那么可以采用线下会议方式开展,但采用开会的方式,一般成本较高,可看团队接受度。
事前&事后:这里指的是发布前还是发布后。版本发布后统一进行CodeReview的方式更多是一种代码交流活动, 起不到代码质量把关的作用。反之,如果在版本发布前就对代码进行CodeReview,就可以对质量问题起到很好的把关作用。这里是时间和质量之间的权衡。
高频率&低频率:笔者建议的是把代码交流放在每一天,所以频率越高越好。具体根据团队实际情况进行安排即可。
此外,也有团队采用模块owner把关质量的CodeReview方式,这种更多是从质量风险规避角度上考虑,在代码提交前owner检查是否有质量问题,确认没有问题后方能发布,有这方面需要的团队也可以考虑这种方式。
最后组合一下,笔者个人推荐的CodeReview方式是强制+事前+小片段+线上交流+高频率,同时,如果能结合线下的大模块方式开展代码交流活动,效果会更好,这个经验来自手机管家高权限应用组的接地气实践。
但是,团队状态不一样,选择方式允许有差异,所以具体选用哪种方式组合,还是要由团队自己作主。
五、结语总结
代码审查主要针对以下几个方面:
1. 代码规范
这一点主要靠Sonarqube。但是有些项目我们使用的是第三方提供的codebase,他们的代码写得就很不规范,被Sonarqube提出一堆warning和error。所以Sonarqube发出的错误提醒邮件我们一般当作参考学习使用,只关心它提出的fatal error。这种error通常是会导致编译失败的。
2. commit message的格式
Git中每一笔commit都需要填写message。团队有规定一套format,大家在提交代码时都需要填写清楚规范。message是可以模板化的东西,因此,做了一个小工具帮助大家生成规范的commit message。使用者只需要下拉菜单里选一下,填几个关键的东西,最后一键生成格式化的message。但是,和机器相比,人是靠不住的。有些人就是不用现成的工具,而要自己手动写,但是又无法保证写的格式正确。不知道这种情况在大家的团队里常见不常见。于是就使用git hook来帮助检查commit message是否符合规范。然而,还想说一遍,人是靠不住的。没办法强制一个人使用git hook,此外,有时会和第三方合作开发,更加没有办法强制他们使用我们的git hook了。所以在Jenkins上也建了一个任务来检查每一笔commit的message是否符合规范,由系统来做判断。
3.代码逻辑
这部分主要在Gerrit上操作了。push完以后,Gerrit上会为每一个commit创建一个分支,并且处于open状态。Reviewer们就可以开始在上面各种发挥了,点开文件,在上面添加comment,然后在replay部分打分并加上些评论。这里除了逻辑,当然也会看代码是否写得规范,是否有语法错误等等,因为Sonarqube目前并没有真正参与到Gerrit的code review中来。如果一切顺利的话,就可以点submit将其merge到真正的branch上去了。如果有问题的话,修改下生成新的patch-set或者abandon掉。
我想介绍下Gerrit。它提供了很多不错的功能:
1. 在网页版的界面上,除了code review操作之外,还能进行rebase、cherry-pick操作。
2. 甚至可以将绝大多数的修改代码的任务直接在Gerrit上完成,选择git repo以及分支,直接创建patch-set开干。然而可惜的是编辑界面实在不够友好。如果修改的部分不是太多,而且不涉及更改文件名字、删除、添加文件的操作的话,可以直接在上面做。
3. 支持权限管理,可以设定不同的用户或用户组能访问哪些branch、可以进行哪些操作。譬如普通用户是无法直接push到refs/heads/上的,只能push到refs/for/上。另外譬如有些用户只能访问A项目的分支,有些用户只能访问B项目的分支。
4. 提供了一些简单封装过的git命令,譬如clone某个git、fetch并且checkout某个commit、fetch并且cherry-pick某个commit、生成某个commit的patch、生成archive等等。
5. 提供查询功能,可以列出符合查询条件的commit。
6. 利用了git分支的命名空间,可以在push的时候选择不同的命名空间分支。譬如常用的refs/for/,创建一个refs/changes/xx/yyyxx/1的分支,待到submit的时候可以其merge到refs/heads/中去,其中yyyxx是每一笔commit在gerrit上唯一的数一个字标识,后面的1或者2或者3代表patch-set的编号;refs/drafts/,可以创建一个draft分支,是无法submit的,后面可以将其转变成refs/for/分支;refs/sandbox//,每个用户可以使用的沙盒分支,名字自定。这种分支上的commit不会参与review,并且没有那么严格的权限限制,可以强制推送,只是不能往别的用户的沙盒分支上推送改动,可以用来备份自己的一些临时分支。这些分支都可以被Jenkins上的各种编译任务用来获取代码,编译其实也是代码审查的一部分。
7. 提供了一组cli命令,和非常多的rest api,这样在命令行和脚本中就可以自由地操作Gerrit了。我用得比较多的是查询然后将数据转变成change note,commit相关信息都可以得到。返回的数据有txt和json两种格式。
8. Gerrit支持加载插件。譬如有个叫gitile的插件就实现了类似git instaweb的功能,可以在网页界面上直接查看仓库各个分支的commit历史。
Code Review在实际开发过程中有时会流于形式,但是在出现重大问题时又会觉得“如果当初好好review就可能避免问题了”的想法。如果可以的话,尽量做,不要因为觉得“可能会浪费时间”而不做,因为最后出了问题浪费的时间更多。
同样,笔者认为,开发编写的Code, 要想让别人更容易读得懂,一定也需要review才有可能达到目的。
所以,我们一起让CodeReview活动这股清流飞起来吧~