欲善其事, 必利其器之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): 分享關於編程、軟件工程、系統架構、前沿技術的藝與術的思考。


相關文章
相關標籤/搜索