欲善其事, 必利其器之Code Quality Checks

进入工业界写代码一转眼就三年多了,在Google写了一年半的Python,C++和borg, 都是本地用Sublime或者用内部的基于web的编辑器cider写,来Squarespace后转到了product engineering,开始大量地写JS和Java,编辑器换成了VSCode和Intellij IDEA。先后差很少都是一年半多的工做时间,但仔细一比发现后一段工做经历不管代码数量仍是质量上都远远超过前一段。缘由挺简单的:项目决定一切。在Squarespace作的几个项目都是咱们Commerce部门里最核心和迫切的几个项目,impact够大,活也够多,能写的代码不少,能学到的东西也就不少了。java

另外一方面,代码写得多并不表明写得好。常常能在代码库git history里看到有人一日几十个commit,每一个commit的描述都是“fix”, “typo”, “test”, “asdf”之类的不成体统的话,点进去一看代码风格和质量也是拆强人意。这种风格的代码放在Google别说merge,申请review都不行,由于好多pre-merge checks都会挂掉。但是在startup,速度即一切,launch是王道,公司还处于苦苦摸索product market fit的阶段,谁会愿意花一个小时争论该用var好仍是const好。FB的motto “Move fast and break things” 可谓一针见血。但是随着公司的成长,工程师团队也迅速地扩大,若是你们在Code Review过程当中对代码质量的把控不严的话,长期以往整个代码库就变得参差不齐, 各类tech debt渐渐累积,重构的难度也会日益加大。git

其实中间有很多现成的工具能够用来帮助整个工程团队维护总体的代码质量,同时大大减轻code reviewer的工做负担。以Java为例,在业界比较经常使用的代码质量校验工具包括:web

  • checkstyle
  • findbugs
  • error-prone
  • PMD
  • jacoco

checkstyle用来维护代码风格一致性和可读性,经常使用的代码风格规范包括Google Java Style, 定义了诸如变量命名规范、格式化。千里之行,始于足下,一致的代码分格对于多人的工程项目是必不可少的,否则长期以往写的人和读的人都很痛苦,老是处在接受别人的风格和坚持本身的风格中挣扎。万一checkstyle报错了呢,你能够手动修正风格错误,也能够用像google-java-format这样的工具自动按照Google Java Style格式化修正。数据库

findbugs是基于Java字节码层面的静态分析工具,是一个从学术界诞生的开源工具。正如其名,它的做用就是帮助工程师发现他们代码中的常见错误和可能的bug。它支持的bug类别主要有:编程

  • Bad practice: 很差的编程习惯,好比类实现了equals方法却没有实现hashcode方法
  • Correctness:正确性,最经典就是经过@Nullable 和 @NoneNull提供更准确的Null Pointer分析
  • Internationalization:国际化,主要是针对String的转换和格式化时一些默认编码和Locale的提醒
  • Malicious code vulnerability:恶意攻击脆弱性,经典的例子就是public类成员是可变的(mutable 或者not final)
  • Multithreaded correctness:多线程正确性,好比在父类constructor里就调用Thread.start()会致使在子类初始化完成前线程就开始了
  • Performance:性能问题,好比内部类没有使用static关键词会致使类实例体积变大
  • Security:安全性问题,好比在源代码中裸用数据库密码
  • Dodgy code:孤僻代码,好比根本没被调用的类方法或者没被使用的类成员


和checkstyle比,findbugs是真刀真枪地检查代码里的问题。即便是写了Java十年的资深工程师有时候也会“栽”在它的坑里,可想而知若是用在一个相对年轻的团队中使用它的做用会有多大。不少bug就能够在被其余人review前就被解决了,code review的效率天然也获得了相应的提升。安全

error-prone和findbugs相似,不过是基于源代码的编译时bug分析工具,由Google的Java Build System组出品。和findbugs比,它的优点有:bash

  • 代码质量高,且有专门的Google组维护,能看到他们在加入更多的bug-pattern。findbugs的社区支持度就相对较弱了。
  • 提供bug fix的建议:由于是基于源代码的分析工具,error-prone有能力基于有bug的源代码提供bug fix的建议。这个功能的开发者体验远胜于findbugs,由于通常findbugs发现一个bug,你还得查他的bug类别文档来理解这个bug pattern而后才能去fix。
  • 基于javac运行,提供了一个error-prone-javac版原本加强标准的Java编译器,这样大部分项目无需不少额外配置就能马上使用。若是用findbugs还得额外设置编译以外的新任务来跑。
  • 完美支持基于Guava的代码分析,毕竟都是狗家的开源项目。

举一个官方的例子:多线程

import java.util.Set;
import java.util.HashSet;

public class ShortSet {
  public static void main (String[] args) {
    Set<Short> s = new HashSet<>();
    for (short i = 0; i < 100; i++) {
      s.add(i);
      s.remove(i - 1);
    }
    System.out.println(s.size());
  }
}
---------------------------------------------------------------------------------------------------------
$ bazel build :hello
ERROR: example/myproject/BUILD:29:1: Java compilation in rule '//example/myproject:hello'
ShortSet.java:6: error: [CollectionIncompatibleType] Argument 'i - 1' should not be passed to this method;
its type int is not compatible with its collection's type argument Short s.remove(i - 1); ^ (see http://errorprone.info/bugpattern/CollectionIncompatibleType) 1 error复制代码

error-prone发现了循环里面的类型不匹配和越界问题。架构

PMD也是基于源代码的分析工具,并且支持多种编程语言:Java, C, C++, JavaScript, Python, PHP等。但可想而知,通用化和特例化就像天平的两端,你只能取其一。相比较findbugs和error-prone,它在Java领域的bug检测能力明显就技不如人。但若是你的代码库是个多种编程语言构成的monorepo,PMD不妨一试。app

jacoco和其余提到的工具不一样,它是用来生成Java代码测试覆盖率报告(test coverage report)的。我之因此把它并列其中是由于代码测试覆盖率也是代码质量的一个很重要的指标。它支持的覆盖率指标包括: 1) 函数覆盖率 2)行覆盖率 3) 分支覆盖率 4)代码库总体覆盖率。 支持JUnit和TestNG两大测试框架,和各类IDE和持续集成系统都良好兼容。若是你用SonarQube的服务的话,能在code review的时候看到每行源代码的测试覆盖程度,很是实用。

若是你用Gradle等构建系统,你还能用jacoco定义最低的测试覆盖率,好比0.7意为着若是测试覆盖率低于70%这个build就会失败。这个最低测试覆盖率听起来有点教条主义了,不过对于有些很是核心的模块多是必不可少的。

说了这么多,但愿能为感兴趣的读者起到初步的启迪做用。有这些这些代码质量检测工具还只是第一步,更重要的是若是塑造一个关心代码质量、追求代码质量的工程师团队文化。只有写代码的人用心了,加上这些工具的辅助,咱们的代码才能达到可读、可用、可测试、可拓展、可维护的理想境界。


2019.1.16


本文原载于: 【技术博客】欲善其事, 必利其器之Code Quality Checks

----------------------------------------------------------------------------------------

艺与术公众号(various__artists): 分享关于编程、软件工程、系统架构、前沿技术的艺与术的思考。


相关文章
相关标签/搜索