查看原文
其他

如何做好CodeReview

孔凡勇 互联网技术集中营 2022-12-29

一、引言

CodeReview对于一个技术团队来讲是非常重要核心的,如果没有一个良好的CodeReview制度,会导致技术债堆积严重,长时间下来影响系统的可维护性、质量以及性能等各方面问题。

良好的的CodeReview习惯,可以有效提高整体代码质量,及时发现代码中可能存在的问题。包括像Google、微软这些公司,Code Review都是基本要求,代码合并之前必须要有人审查通过才行。


尤其对于一个创业团队来讲做好CodeReview真心不容易,创业团队要追求业务快速增长,一方面从上到下很少人会重视,认真做CodeReview的很少,有的流于形式,有的可能根本就没有Code Review的环节,代码质量只依赖于事后的测试。也有些团队想做好代码审查,但不知道怎么做比较好。


二、CodeReview作用

其实大部分人都知道CodeReview的作用。这里简述下几点主要作用:

①尽量避免bug的出现。只能尽量降低bug的"产出率",提前发现bug,降低生产事故发生率。

②方便维护,降低维护成本。编写规范的代码和优秀的代码可以给未来接手的同学更好的理解其意图进行维护或者在其基础上继续开发,不会让新同学想“打死前任”的冲动。

③正确处理业务需求逻辑。开发同学实现的业务需求是否与需求对应,是否会存在理解偏差,是否存在处理不当等等。

④尽量最优实现。在编写代码的过程中,每个人的逻辑思维都可能存在不同,在评审过程中,开发同学写的代码还存在现有更好、更优的实现方式,或者把实现的最优逻辑分享给其它同学讨论学习。


三、CodeReview相关工具

在正式代码Review之前,推荐阿里Java代码开发规范P3C进行扫描一遍:https://github.com/alibaba/p3c/,都有相应的Eclipse/IDEA插件。

推荐使用ReviewBoard:https://github.com/reviewboard/reviewboard

ReviewBoard支持pre commit和post commit,条件允许的情况下推荐使用pre commit,时间紧迫的情况可以使用post commit。


四、如何推进CodeReview

1.把CodeReview作为开发流程的必选项而不是可选项

在很早以前,我就尝试过将代码审查作为代码流程的一部分,但只是一个可选项,没有Code Review也可以把代码合并到master。这样的结果就是想起来才会去做Code Review,去检查的时候已经有了太多的代码变更,审查起来非常困难,另外就算审查出问题,也很难得以修改。


我们现在对代码的审查则是作为开发流程的一个必选项,每次开发新功能或者修复Bug,开一个新的分支,分支要合并到master有两个必要条件:

1)所有的自动化测试通过

2)有至少一个人CodeReview通过,如果是新手的PR,还必须有资深程序员CodeReview通过


这样把CodeReview作为开发流程的一个必选项后,就很好的保证了代码在合并之前有过Code Review。而且这样合并前要求代码审查的流程,好处也很明显:

1)由于每一次合并前都要做代码审查,这样一般一次审查的代码量也不会太大,对于审查者来说压力也不会太大

2)如果在CodeReview时发现问题,被审查者希望代码能尽快合并,也会积极的对审查出来的问题进行修改,不至于对审查结果太过抵触


2.把CodeReview变成一种开发文化而不仅仅是一种制度

把Code Review 作为开发流程的必选项后,不代表CodeReview这件事就可以执行的很好,因为Code Review的执行,很大部分程度上依赖于审查者的认真审查,以及被审查者的积极配合,两者缺一不可!


如果仅仅只是当作一个流程制度,那么就可能会流于形式。最终结果就是看起来有CodeReview,但没有人认真审查,随便看下就通过了,或者发现问题也不愿意修改。

真要把CodeReview这件事做好,必须让CodeReview变成团队的一种文化,开发人员从心底接受这件事,并认真执行这件事。

要形成这样的文化,不那么容易,也没有想象的那么难,比如这些方面可以参考:


首先,得让开发人员认识到CodeReview这件事为自己、为团队带来的好处

然后,得要有几个人做好表率作用,榜样的力量很重要

还有,对于管理者来说,你激励什么,往往就会得到什么

最后,像写自动化测试一样,把CodeReview要作为开发任务的一部分,给审查者和被审查者都留出专门的时间去做这件事,不能光想着马儿跑得快又舍不得给马儿吃草


如何形成这样的文化,有心的话,还有很多方法可以尝试。只有真正让大家都认同和践行,才可能去做好Code Review这件事。


3.CodeReview需要注意事项

CodeReview沟通常见问题:

1)把在CodeReview中别人给你提的修改意见当成了对自己能力的否定,以为对方是在说自己是个不合格的程序员。这样就会在工作中带有强烈的个人情绪,导致整个CodeReview环节气氛变的尴尬无比

2)由于CodeReview是书写方式,被review的人在看到你的批注时候,经常会多想,明明是一句简单的批注:此处应该关闭流。当事人看到也会理解为:我靠,这么简单的都忘记了,连关闭流都不知道,真挫

3)你要求的修改点在对方看来是鸡蛋里挑骨头,或是对方觉得我这样实现就是很好,为什么要改。冲突就这样产生,如果这个时候没处理好,直接就会导致冲突的上升,就不仅仅是技术讨论的问题。


个人情感的代入是批注部分最难的问题根源之一。

不要凸显自己的牛X,自己技术比别人好的优越感。抛开这种优越感。记住CodeReview的主要目的不仅仅为了找代码的bug, 而是提供认真专业的反馈来帮助队友提高,你的每条建议都是给双方一次学习的机会。


建议:

1)在CodeReview的批注中加入code sample

2)在批注中尽量不要用指代人称,如 你****,如果要用可以用我们来替代你。用我们能强化团队的整体意思,而你则更多带有指责的意思

3)尽量不要用命令的口吻去表达自己的意见。如果你的公司阶级观念比较重,那就另当别论。

4)CodeReview评论要友好,避免负面词汇;有说不清楚的问题当面沟通。

5)批注中对要改的内容作出说明原因时候,尽量能有理论依据支撑,而不是我觉得就应该这样。这样做到有理有据,不会出现相互不服问题


五、常见CodeReview问题总结


1.基础规范

比如团队制定一些内部规范,比如代码模板、代码分层结构、内部组件库使用、开源软件使用规范等,这些基本的前提是要遵守的,这一点是CodeReview前提。


1)统一的代码format格式

这里不讨论哪种代码风格好,比如右花括号是单独一行显示好,还是在不用哪个换行的好。个人觉得团队需要做的不是是花时间讨论代码format格式没有哪种好哪种差,而是需要制定一种格式风格,然后团队每个开发成员去遵守即可,推荐使用阿里巴巴的P3C插件的code format,如果代码format格式执行不到位,后面代码merge成本会非常高。


2)统一的代码提交comment风格

如果是做的是一个功能,需要注明本次提交做的是哪个功能,实现思路是什么

如果是修改bug的,需要注明本次提交修改了什么bug, bug的原因以及如何修改的

这样就方便Code Review的人能很清楚的知道你的代码思路

推荐使用angular的代码提交规范:https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html


3)代码风格

方法名:在计算机科学中,命名是一个难题。一个函数被命名为==get_message_queue_name==,但做的却是完全不同的事情,比如从输入内容中清除html,那么这是一个不准确的命名,并且可能会误导。

值名:对于数据结构,==foo== or ==bar== 可能是无用的名字。相比==exception==, ==e==同样是无用的。如果需要(根据语言)尽可能详细,在重新查看代码时,那些见名知意的命名是更容易理解的。

函数长度:对于一个函数的长度,我的经验值是小于20行,如果一个函数在50行以上,最好把它分成更小的函数块。

类的长度:我认为类的长度应该小于300行,最好在100内。把较长的类分离成独立的类,这样更容易理解类的功能。

文件的长度:对于Python,一个文件最多1000行代码。任何高于此的文件应该把它分离成更小更内聚,看一下是否违背的“单一职责” 原则。

文档:对于复杂的函数来说,参数个数可能较多,在文档中需要指出每个参数的用处,除了那些显而易见的。

注释代码:移除任何注释代码行。

函数参数个数:不要太多, 一般不要超过3个。。

可读性:代码是否容易理解?在查看代码时要不断的停下来分析它?


以上相关的规范推荐使用阿里巴巴的P3C插件定义的规范。


4)使用异常还是错误码返回

在分布式微服务应用中,系统调用依赖关系复杂,系统内部之间抛异常传递,系统与系统之间使用错误码返回,这样能够清晰看到异常错误原因、定位问题。


5)统一错误码

错误码本身不算是代码问题,不过基于整个组织和工程的可维护性来说,可以将错误码不符合规范作为一种错误加以避免。方法:对错误码进行可控的管理和遵循规范使用。可以使用公共文档维护, 也可以开发错误码管理系统来避免相同的错误码。


6)参数检测 

参数检测是对业务处理的第一层重要过滤。如果参数检测不足够,就会导致脏数据进入服务处理,轻则导致异常,重则插入脏数据到数据库,对后续维护都会造成很多维护成本。

方法:采用“契约式编程”,规定前置条件,并使用单测进行覆盖。


对于复杂的业务应用, 优雅的参数检测处理尤为重要。根据 “集中管理和处理一致性原则”, 可以建立一个 paramchecker包或者基于hibernate validator进行参数检测, 设计一个可复用的微框架来对应用中所有的参数进行统一集中化检测。

参数检测主要包括: 

(1) 参数的值类型, 可以根据不同值类型做基础的检测; 

(2)参数的业务类型,有基础非业务参数, 基础业务参数和具体业务参数。不同的参数业务类型有不同的处理。将参数值类型与参数业务类型结合起来, 结合一致性的异常捕获处理, 就可以实现一个可复用的参数检测框架。参数检测既可以采用普通的分支语句,也可以采用注解方式。采用注解方式更可读,不过单测编写更具技巧。


7)小而完整的代码提交习惯

很多开发很怕麻烦,觉得修改一个bug需要重新创建一个分支并提交一次pullrequest, 太浪费时间。但是不知道是,一次提交太多的代码或是多个功能或是多个bug一起提交,会导致code review的效果大打折扣。建议一次提交的改动尽量不要超过100行,否则review起来花的时间会特别长,同时也会导致code review的人失去耐心,从而没有真正起到review的作用。


2.代码业务逻辑

我们在Code Review的过程中,也根据实际业务进行总结了跟业务紧密关联的问题,也就是业务开发规范。在业务处理的时候,根据业务特点或者业务处理方式,我们需要按照实际业务去调用我们规定的方式,因为曾多次出现,所以做下记录,防止后人或新人重复掉坑里,甚至重复出现同一类问题事故。同时,我们针对总结的问题,也按照建议和强制的规范级别来要求或者指导我们的同学进行更好的开发。


业务逻辑正确这是最基本的前提,如果逻辑存在缺陷肯定会出现bug,所以这是CodeReview的基本前提。


3.体系结构和代码设计

常见的Code Review的设计问题如下分类:

单一职责原则:一个类有且只能一个职责。我通常使用这个原则去衡量,如果我们必须使用“和”来描述一个方法做的事情,这可能在抽象层上出了问题。

开闭原则:如果是面向对象的语言,对象对可扩展开放、对修改关闭。如果我们需要添加另外的内容会怎样?

代码复用:根据"三振法",如果代码被复制一次,虽然如喜欢这种方式,但通常没什么问题。但如果再一次被复制,就应该通过提取公共的部分来重构它。

潜在的bugs:是否会引起的其他错误?循环是否以我们期望的方式终止?

错误处理:错误确定被优雅的修改?会导致其他错误?如果这样,修改是否有用?

效率:如果代码中包含算法,这种算法是否是高效?例如,在字典中使用迭代,遍历一个期望的值,这是一种低效的方式。


有些新人发现自己的代码提交PR(Pull Request)后,会收到一堆的Code Review意见,必须要做大量的改动。这多半是因为在开始做之前,没有做好设计,做出来后才发现问题很多。

建议在做一个新功能之前,写一个简单的设计文档,表达清楚自己的设计思路,找资深的先帮你做一下设计的审查,发现设计上的问题。设计上没问题了,再着手开发,那么到Review的时候,相对问题就会少很多,这也同时体现了技术方案评审的重要性。


4.可维护性

可维护性问题是“在当前业务变更的范围内通常不会导致BUG、故障,却会在日后埋下地雷,引发BUG、故障、维护成本大幅增加”的类别。

1)硬编码

硬编码主要有三种情况:a. “魔数”;b. 写死的配置;c. 临时加的逻辑和文案。

“魔数”与重复代码类似,当前或许不会引发问题,时间一长,为了弄清楚其代表的含义,增加很多沟通维护成本,且分散在各处很容易导致修改的时候遗漏不一致。务必清清除。方法也比较简单:定义含义明显的枚举或常量,代表这个魔数在代码中发言。

“写死的配置”不会影响业务功能, 不过在环境变更或系统调优的时候,就显得很不方便了。方法:尽量将配置抽离出来做成配置项放到配置文件里。

“临时加的逻辑和文案”也是一种破坏系统可维护性的做法。方法:抽离出来放在单独的函数或方法里,并特别加以注释。

 

2)重复代码

重复代码在当前可能不会造成 BUG,但上线后,需要维护多处的事实一致性;时间一长,后续修改的时候就特别容易遗漏或处理不一致导致 BUG;重复代码是公认的“代码坏味”,必当尽力清除。方法:抽离通用的部分,定制差异。重复代码还有一种情况出现,即创造新函数时,先看看是否既有方法已经实现过。

 

3)通用逻辑与定制业务逻辑耦合

这大概是每个媛猿们在开发生涯中遇到的最恶心的事情之一了。通用逻辑与具体的各种业务逻辑混杂交错,想插根针都难。遇到这种情况,只能先祈福,然后抽离一个新的函数,严格判断相应条件满足后去调用它。

如果是新创建逻辑,可以使用函数式编程或基于接口的编程,将通用处理流程抽离出来,而将具体业务逻辑以回调函数的形式传入处理。

不要让不同的业务共用相同的函数,然后在函数里一堆 if-else plus switch , 而是每个业务都有各自的函数, 并可复用相同的通用逻辑和流程处理;或者各个业务可以覆写同样命名的函数。

复用,而非混杂。

 

4)直接在原方法里加逻辑

有业务改动时,猿媛们图方便倾向于直接在原方法里加判断和逻辑。这样做是很不好的习惯。一方面,增加了原方法的长度,破坏了其可维护性;另一方面,有可能对原方法的既有逻辑造成破坏。可靠的方式是:新增一个函数,然后在原方法中调用并说明原因。


5)多业务耦合

在业务边界未仔细划分清晰的情况下出现,一个业务过多深入和掺杂另一个非相关业务的实现细节。在项目和系统设计之初,特别要注意先划分业务边界,定义好接口设计和服务依赖关系,再着手开发;否则,延迟到后期做这些工作,很可能会导致重复的工作量,含糊复杂的交互、增加后期系统维护和问题排查的许多成本。磨刀不误砍柴工。划分清晰的业务、服务、接口边界就属于磨刀的功夫。

 

6)代码层次不合理

代码改动逻辑是正确的,然而代码的放置位置不符合当前架构设计约定,导致后续维护成本增加。

代码层次不合理可能导致重复代码。比如获取操作人和操作记录,如果写在类 XController 里, 那么类 YController 就面临尴尬局面:如果写在 YController , 就会导致重复代码;如果跨层去调用 XController 方法,又是非常不推荐的做法。因此, 获取操作人和操作记录,最好写在 Service 层, Controller 层只负责参数传入、检测和结果转译、返回。


7)不用多余的代码

工程中常常会有一些不用的代码。或者是一些暂时未用到的Util工具或库函数,或者是由于业务变更导致已经废弃不用的代码,或者是由于一时写出后来又重写的代码。尽量清除掉不用多余的代码,对系统可维护性是一种很好的改善,同时也有利于CodeReview。


8)使用全局变量

使用全局变量并没有“错”,错的是,一旦出现问题,排查和调试问题起来,真的会让人“一夜之间白了头”,耗费数个小时是轻微惩罚。此外,全局变量还能“顺手牵羊”地破坏函数的通用性,导致可维护性变差。务必消除全局变量的使用。当然,全局常量是可以的。


9)缺乏必要的注释

对重要和关键点的代码缺乏必要的注释,使用到的重要算法缺乏必要的引用出处,对特别的处理缺乏必要的说明。

原则上, 每个方法至少要用一个简短的单行注释, 适宜地描述了方法的用途、业务逻辑、作者及日期。对于特殊甚至奇葩的需求的特别实现,要加一些注释。这样后续维护时有个基础。


5.空值

空值恐怕是最容易出现的地方之一。常见错误有: 

a. 值为NULL导致空指针异常; 

b. 参数字符串含有前导或后缀空格没有Trim导致查询为空。导致以上结果的原因主要有:无此记录、有此记录但由于SQL访问异常而没查到、网络调用失败、记录中有脏数据、参数没传。


原则上,对于任何异常, 希望能够打印出具体的错误信息,根据错误信息很快明白是什么原因, 而不是一个 null ,还要在代码里去推敲为什么为空。这样我们必须识别出程序中可能的null, 并及时检测、捕获和抛出异常。

对于空值,最好的防护是“防御式编程”。当获取到对象之后, 使用之前总是判断是否为空,并适当抛出异常、打错误日志或做其它处理。


6.异常处理

常见的异常处理规范包括如下:

1)尽量用try…catch…finally的语句来处理异常,在finally应当尽可能回收内存资源。

2)尽量减少用try监控的代码块。

3)先用专业的异常来处理,最后再用Exception异常来兜底。

4)在catch从句里,别简单地抛出异常了事,应当尽可能地处理异常,避免吞掉异常不做任何处理。

5)出现异常后,应尽量保证项目不会终止,应尽量把异常造成的影响缩小到最小程度。

6)遇到异常如果无法处理,逐层往上抛

7)未捕获潜在的异常,调用API接口、库函数或系统服务等,只顾着享受便利却不做防护,常导致因为局部失败而影响整体的功能。最好的防护依然是“防御式编程”。要么在当前方法捕获异常并返回合适的空值或空对象,要么抛给高层处理。


7.日志打印

对于重要而关键的实例状态、代码路径及API调用,应当添加适当的Info日志;对于异常,应当捕获并添加Error日志。缺乏日志并不会影响业务功能,但出现问题排查时,就会非常不方便,甚至错失极宝贵的机会(不易重现的情况尤其如此)。此外,缺乏日志也会导致可控性差,难以做数据统计和分析。

同时打印过多的日志并不好,一方面遮掩真正需要的信息,导致排查耗费时间, 另一方面造成服务器空间浪费、影响性能。生产环境日志一般只开放 INFO及以上级别的日志;Debug 日志只在调试或排错的时候使用,生产环境可以禁止debug日志。


8.并发问题

并发的问题更难检测、复现和调试。常见的问题有:

1)在可能由多线程并发访问的对象中含有共享变量却没有同步保护;

2)在代码中手动创建缺乏控制的线程或线程池

3)并发访问数据库时没有做任何同步措施

4)多个线程对同一对象的互斥操作没有同步保护


使用线程池、并发库、并发类、同步工具而不是线程对象、并发原语。在复杂并发场景下,还需注意多个同步对象上的锁是否按合适的顺序获得和释放以避免死锁,相应的错误处理代码是否合理。

并发问题还特别容易导致线程安全问题,在作为全局变量使用需要特别注意,比如常见的HasMap、SimpleDateFormat非线程安全对象避免作为全局变量使用。


9.幂等问题

幂等问题也是分布式系统中经常遇到的问题,客户端或者网络会触发一些重试场景,如果程序没有做到幂等,会导致一些脏数据或者bug问题,需要抽象出一个公共幂等组件针对该场景进行处理。


10.资源泄露

资源泄露通常有以下几种情况:

1)打开文件却没有关闭

2)连接池的连接未回收

3)重复创建的脚本引用没有置空,无法被回收

4)已使用完的集合元素始终被引用,无法被回收


11.事务问题

事务方面常出现的问题是:

1)多个紧密关联的业务操作和 SQL 语句没有事务保证。 

2)在资金业务操作或数据强一致性要求的业务操作中,要注意使用事务,保证数据更新的一致性和完整性。

3)涉及到数据库事务和其他远程调用问题,比如文件上传是先保存数据库还是先进行文件上传,文件上传不属于数据库事务,如何保证一致性是个需要解决问题。


12.性能问题

低性能会导致产品功能不好用、不可用,甚至导致产品失败。

常见情况有:

a. 循环地逐个调用单个接口获取数据或访问数据库; 

b. 重复创建几乎完全相同的(开销大的)对象;

c. 数据库访问、网络调用等服务未处理超时的情况; 

d. 多重循环对于大数据量处理的算法性能低;

e. 大量字符串拼接时使用了String而非StringBuilder.


对于 a,最好提供批量接口或批量并发获取数据; 

对于 b, 将可复用对象抽离出循环,一次创建多次使用; 

对于 c,设置合理的超时时间并捕获超时异常处理; 

对于 d,使用预排序或预处理, 构造合适的数据结构, 使得算法平均性能在 O(n) 或 O(nlogn) ; 

对于 e, 记住:少量字符串拼接使用String, 大量字符串拼接使用 StringBuilder, 通常不会使用到 StringBuffer.


当然这里面还有更多的涉及到性能问题,比如一些工具类或JDK使用:Apache的BeanUils比Spring 的BeanUils性能低,使用Java中的正则要进行预编译等。。。


13.SQL问题

SQL的正确性通常可以通过 DAO 测试来保证。SQL问题主要是指潜在的性能问题和安全问题。

要避免SQL性能问题, 在表设计的时候就要做好索引工作。在表数据量非常大的情况下,SQL语句编写要非常小心。查询SQL需要添加必要索引,添加合适的查询条件和查询顺序,加快查询效率,避免慢查;尽量避免使用 Join, 子查询;避免SQL注入。

尤其避免在 update 语句中使用 where-if !很容易导致全表更新和严重的数据丢失,造成严重的线上故障 !!!

另外一点避免使用存储过程,以及SQL中写业务逻辑。


14.安全

安全问题一向是互联网产品研发中极容易被忽视、而在爆发后又极引发热议的议题。安全和隐私是用户的心理红线之一。应用、数据、资金的安全性应当仅次于产品功能的准确性和使用体验。

常见的安全问题包括XSS、CSRF以及其他越权等,关于XSS可以定义全局的XSSFilter或者在网关层实现xss过滤,其他CSRF和越权问题在编码时需要格外注意,否则会引发安全漏洞,造成不必要的损失。

另外还有一些常见问题:缓冲区溢出;恶意代码注入;权限赋予不当;应用目录泄露等。


安全问题的CodeReview可参见检查点清单:信息安全 。主要是如下措施: 

a. 严格检查和屏蔽非法输入; 

b. 对含敏感信息的请求加密通信; 

c. 业务处理后消除任何敏感私密信息的任何痕迹; 

d. 结果返回前在反序列化中清除敏感私密信息; 

e. 敏感私密信息在数据存储设备中应当加密存储; 

f. 应用有严格的角色、权限、操作、数据访问分级和控制; 

g. 切忌暴露服务器的重要的安全性信息,防止服务器被攻击影响正常服务运行。


15.设计问题

常见的设计问题通常体现在:

1)是否有潜在的性能问题

2)是否有安全问题

3)业务变化时是否容易扩展

4)是否有遗漏的点

5)持续高负荷压力下是否会崩溃


五、CodeReview实践

具体CodeReview时要先Review设计实现思路,然后Review设计模式,接着Review成形的骨干代码,最后Review完成的代码,如果程序复杂的话,需要拆成几个单元或模块分别Review。


另外在CodeReview之前需要强调几点:

1)代码在提交CodeReview之前,要自己先REVIEW和测试一遍

2)提交PR要小

在做Code Review的时候,如果有大量的文件修改,那么Review起来是很困难的,但如果PR比较小,相对就比较容易Review,也容易发现代码中可能存在的问题。所以在提交PR时,PR要小,如果是比较大的改动,那么最好分批提交,以减轻审查者的压力。

3)对评论进行分级

在做CodeReview时,需要针对审查出有问题的代码行添加评论,如果只是评论,有时候对于被审查者比较难甄别评论所代表的含义,是不是必须要修改。

4)评论要友好,避免负面词汇;有说不清楚的问题当面沟通


最佳实践建议:

1)制定规范:编码规范+技术规范+业务规范+数据库规范,形成标准文档化。

2)根据需求开发,定期安排相关开发人员、评审人员参与Code Review,有需要的话可以邀请测试人员参与。

3)开始评审前,要求相关代码通过阿里编码规范/FIndBugs/SonarLint等插件进行前期扫描,避免出现比较多的基本问题。

4)新同学首次参入进来,需要着重关注新人,给予更好的建议,帮助提升代码质量。

5)针对评审质量很高的代码给予肯定,并且表扬相关人员。

6)定期总结Code Review存在的问题,记录相关问题于wiki(文档)上,按月度或季度定期输出总结。

7)制定与实际业务相关的开发规范,防止重复掉坑,重复出现同一类问题事故等。


这是CodeReview一些工作心得,如果觉得哪里说的不好,或者有其它见解,欢迎评论,大家一起探讨。



reference:

https://www.cnblogs.com/lovesqcc/p/9271781.html

https://toutiao.io/posts/x9rd6d/preview

https://blog.csdn.net/cm15835106905/article/details/104690023

https://zhuanlan.zhihu.com/p/73809355

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存