怎么提高代码质量?-来自Google的研发经验总结

12,015 阅读12分钟

你的团队有没有过这样的经历:开发效率低,招了很多人,天天加班,出活却不多,线上bug频发,领导发飙,中层束手无策,工程师抱怨不断,查找bug困难。其实这些都是代码质量差惹的祸。代码质量是研发质量管理的根本,它决定了整个开发团队的开发效率,项目质量,其他监控,告警,日志等手段都只能是事后补偿。本文就如何保证代码质量总结了一些经验和方法,供大家参考。

代码质量本身并没有一个特别明确的量化指标,而且根据公司发展的不同阶段,团队规模的大小不同,项目性质的不同等,对代码质量的要求也不尽相同.不过如果项目中出现以下情况时候,就说明代码质量要值得重视了.

  • 添加或修改一个简单功能时,涉及要修改的地方特别多,而且很分散;
  • 代码不可复用:相似的功能无法复用代码,要重新开发;
  • 线上bug频发,排错困难,修复难度大,时间长;
  • 有很多奇怪的代码,代码读不懂,新人无法很快了解代码;
  • 代码中坑特别多,不敢大动,一不小心就踩坑;

    以上这些问题,基本上都是代码质量不高导致的,包括代码无注释,无文档,命名差,项目层次结构差,调用关系混乱,到处hardcode,临时解决方案等等。怎么才能时刻保证代码的高质量,避免以上问题发生?当然团队的技术素质很重要,除此之外,还有一些方法可循的.

    1. 吹毛求疵般地执行编码规范

    严格执行代码编写规范,可以使一个项目乃至一个公司的代码具有完全统一的风格,就像同一个人编写的一样,而且命名良好的变量,函数,类和注释,也无疑可以提高代码的可读性.具体落实到执行层面,可以参照Google的编码规范或者java官方的编码规范,网上可以找到,关键是要严格遵守,并且在code review时,严格要求,没有按照规范的一定要指出并且要求修改.

    实际情况往往是虽然大家都知道优秀的代码规范是怎样的,但在具体写代码的过程中,却执行的差强人意,很多情况是认识上不够重视,觉得一个变量或者函数的命名成哪样关系不大,所以不够推敲,注释很多也都不写,code review的时候大家也都事不关己心态,或者觉得没必要太抠细节,导致慢慢的整个code base变得越来越差.所以这里还是要强调一下,细节决定成败,提高团队对代码规范的认同及其严格的执行是关键.

    2. 编写高质量的单元测试

    单元测试是最容易执行,且对提高代码质量见效最快的方法之一还。但还是有很多公司对单元测试重视不够,包括一些大的互联网公司,不写或者随便写写。

    有些工程师觉得有测试团队就够了,再写单元测试就是浪费时间。其实测试团队的测试和单元测试是在不同层面上的,测试团队的测试一般是黑盒测试,系统层面的集成测试,对于复杂系统来说,组合爆炸,测试团队无法穷举所有的测试用例。单元测试是代码层面的测试,一般是针对类的测试。既然无法从系统的整体上保证100%符合我们的预期,那单元测试起码能保证我们代码在细粒度上运行符合预期。

    有些工程师认为开发任务重没时间写。这个还是没有足够重视单元测试,觉得是可有可无的部分,才会有这样的想法。写好单元测试,节省很多解决线上bug的时间,开发时间反而更充足了。

    还有很多工程师虽然在写单元测试,但只对正常流程做测试。代码中的bug多数是写代码时异常情况没有考虑全面导致的,正常流程一般不会出问题。单元测试的作用就在于测试各种异常情况下代码的运行是否符合预期,所以只对正常流程测试无法发挥单元测试真正的作用。

    一般情况下,单元测试代码量要比要测试的代码多,一般是1-2倍的样子,写单元测试本身没有太多的技术挑战,主要看工程师逻辑是否缜密,能够考虑各种异常情况,写起来比较枯燥,所以写高质量的单元测试的一方面要靠工程师的耐心执行,另一方面要靠团队的严格要求。当然这些都是建立在对单元测试重要性的认同之上。

    3. 不流于形式的Code Review

    如果说单元测试很多工程师不怎么重视,那code review就是不怎么接受.跟很多大型互联网公司的人聊过,对code review都不怎么认可,大部分反应都是,这玩意不可能很好的执行,浪费时间,是的,code review做的再流畅,也是要花时间的,关键在于我们是愿意花2天写代码花5天修bug,还是愿意花3天写代码花半天修bug.

    其实,code review的好处不仅仅是能够大大提高代码质量,减少代码bug,你想想如果我们没有code review,平时写的代码“偷偷”就commit了,难免有人不自律,有了code review,直播代码,曝光dirty code,大家就会更认真些.其次来讲,code review也是一种有效技术传帮带的途径,每次code review都是一次案例的剖析,可以帮助初级的工程师培养编码规范,提高编码质量,设计能力甚至于架构能力,反过来,review别人写的好的代码,对自己也是一种学习和提高.

    除此之外,严格的code review不仅能保证代码的质量,还能形成良好的技术氛围。

    4. 开发未动文档先行

    编写技术文档对大部分工程师来说都是挺反感的事情。一般来讲在开发某个系统或者重要模块或者功能之前需要先写技术文档,然后发送给同组或者相关同事审查,在审查没有问题的情况下再开发,这样能够事先达成共识,开发出来的东西不至于走样,而且当开发完成之后进行code review的阶段,代码审查者通过阅读开发文档也可以快速的理解代码.

    除此之外,文档对于团队和公司来讲都是重要的财富,对于新人加入公司熟悉代码,产品,对于任务的交接等等都很有帮助,而且作为一个规范化的技术团队,技术文档是一种摒弃作坊式开发和个人英雄主义的有效方法,是保证团队有效协作的途径.

    不过,有很多工程师提出说不会写技术文档,不知道写什么,希望给一个模板或者目录.我之前曾经想过是否可以给出一个固定的模板,但最后还是放弃了,比较难,难点在于,每个项目侧重点都不一样不容易总结,如果硬要给出一个很宽泛的目录,不具有指导性也没有意义.大体上来讲,文档的内容主要是将做的东西讲清楚,包括出问题背景,解决了什么问题,外部怎么用或调用,内部如何实现,大的架构,关键功能和算法等,以及一些非功能性的考虑。

    5. 持续重构,重构,重构

    个人比较反对平时不注重代码质量,堆砌烂代码,实在维护不了了就大刀阔斧的重构甚至重写。有时候项目代码太多了,重构很难做到彻底,最后又搞出来一个四不像的怪物,更麻烦了!

    优秀的代码或架构不是一开始就能完全设计好的,就像优秀的公司或产品也都是迭代出来的一样的,我们无法100%遇见未来的需求,也没有足够的精力,时间,资源为遥远的未来买单,所以随着系统的演进,重构代码也是不可避免的,虽然上面说了不支持大刀阔斧推到重来式的大重构,但持续的小重构还是比较推崇的,也是时刻保证代码质量防止代码腐化有效手段.简单一句话就是不要等到问题堆得太多了再采取重构,要时刻有人对代码整体负责任,平时没事就改改代码,而不要觉得重构代码就是浪费时间,不务正业!

    特别是一些业务开发团队,有时候为了快速完成一个产品或者业务功能,只追求速度,到处hard code,在完全不考虑非功能性需求的情况下,堆砌一些烂代码,这种情况还是比较常见的。不过没关系,等有时间一定要记着重构,不然烂代码越堆越多,总有一天会没人能维护。

    6. 项目与团队”微服务化”

    只有小项目是可以维护的,大项目是无法维护的.团队人比较少的时候,十几个人的样子,代码量也不多,不超过10万行,怎么开发,怎么管理都没问题,大家互相都了解彼此做的东西,代码质量太差了,大不了重写一遍.但如果是一个极其庞大的项目,几十万行代码,几十个开发维护,那基本上没人能对代码负责了.

    所以当项目太大了之后,就需要对代码和团队进行拆分,模块化,大团队拆成几个小团队,大项目拆成几个小项目,这样每个团队每个项目的代码都不至于很多,也不至于出现代码质量太差无法维护的情况,其实很多技术也都体现了这种思想,比如大到soa, 微服务,小到jar, .so等lib模块开发,Class类的封装,都是一种拆分的思想.

    7. 重视代码关注细节

    以上其他的所有方法都是治标不治本,找到对的人用好对的人,打造优秀的技术文化,才是能一直卓越的根本。有很多工程师比较热衷于学习架构,工具,框架层面的东西,见过很多工程师,还没写三五年代码就转做架构师,不写代码了,到处忽悠,很不好,互联网信息如此透明,不同的人去做同一个项目,其实最后设计出来的架构,功能大约都差不多,最后大家都能把这个系统实现,但有些人做出来的系统,bug很多,性能很差,扩展性也不好,最多能叫个POC。

    高手之间的竞争还是在于细节,一个算法够不够优化,数据存取的效率高不高,内存是否够节省等等,这是累积起来决定了一个系统是不是够优秀。

    当然并不是说框架,工具,架构设计这些方面的学习不重要,关键是有深度,希望是实践中锻炼得来的,而不是到处看微信公众号,博客得来的。

    国内工程师普遍深度不够,做几年技术就转管理或者纯架构设计不写代码了,而国外不一样,大龄码农很多,所以国外的优秀开源项目比较多,而国内很少。

    8. 工欲善其事必先利其器

    代码中的很多低级质量问题不需要人工去审查,java开发有很多现成的工具可以使用,比如:checkstyle,findbugs, pmd, jacaco, sonar等。

    Checkstyle,findbugs,pmd是静态代码分析工具,通过分析源代码或者字节码,找出代码的缺陷,比如参数不匹配,有歧义的嵌套语句,错误的递归,非法计算,可能出现的空指针引用等等。三者都可以集成到gradle等构建工具中。

    Jacoco是一种单元测试覆盖率统计工具,也可以集成到gradle等构建工具中,可以生成漂亮的测试覆盖率统计报表,同时Eclipse提供了插件可以EclEmma可以直观的在IDE中查看单元测试的覆盖情况。

    Sonar Sonar 是一个用于代码质量管理的平台。可以在一个统一的平台上显示管理静态分析,单元测试覆盖率等质量报告。

    最后,总结

    以上所有的这些方法论应该都没啥新奇的,也没有葵花宝典似的杀手锏,说出来感觉都很简单的,现在互联网这么发达,信息都很透明,所以大方向大家都知道,具体的策略和架构各家也都差不多,最后谁做的好,关键在于执行和细节,经常听到有人说我们做了单元测试啊,我们做了性能测试,可最后还是一堆性能问题一堆bug,那就要去考虑一下到底做的够不够好,是否做到了具体问题具体分析,不生搬硬套,从决策到执行再到考核是否形成了闭环,很多时候只是空喊口号,口号喊得100分,落实到执行只能得50分,最后又完全没考核,好坏大家也都不知,切记敏于言而讷于行。

    作者王争,前Google工程师,15万人订阅的《数据结构和算法之美》《设计模式之美》作者。微信公众号:小争哥,关注微信公众号回复PDF,获取100+页Google工程师的算法学习和面试经验分享。