Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

Impact of error-prone

Findbugs is really good solution to improve quality of code

As an author of findbugs-slf4j, I really love Findbugs. It lets us find problems by static bytecode analysis. This tool improves quality of each module, reduce reproducing known bug, and point problem quickly in IDE. We do not have to use code review to find known problems and/or antipatterns, Findbugs will do it before developer sends pull-request. This 'push notification' is really cool, we can point problem even if developer has no motivation to find it.

It is really ideal solution also for large scale development team. Even if your team has hundreds developers, they can get help from Findbugs from the beginning of working day. Even though some of them have less experience and knowledge about our depending framework, module and language, Findbugs can help them to catch up quickly.

Findbugs is constructed based on both academic and practical experience, and now it's OSS so not only limited contributors but also everyone who wants to improve documentation comment, message translation, new detectors and so on.

Problems in Findbugs

But I have a dream to implement Findbugs alternative, to replace Findbugs with better implementation. In my personal opinion, it has some problems:

  • Difficult to maintain: Less documentation comment to describe usage*1, Less fresh document to describe (e.g. how to implement plugin), Too wide scope,
  • Slow performance: Not ready to use multi-thread so efficiently. No support for incremental analysis provided by build tools.
  • Difficult to test: To test Findbugs plugin, test-driven-detectors4findbugs was good solution but it doesn't work with Findbugs 3.0.1 due to internal implementation change. We also need a hack to test by Maven.

Yes Findbugs is OSS, so in theory we can solve them by own. But recently this project is not so active, and there is no popular fork for now. It would be problem to solve.

New alternative: error-prone

Last week I found an interesting static analysis tool, named error-prone.

github.com

This does not check bytecode, it applies hack to javac so it handles AST under com.sun.source.tree package. In my understanding, it has many good points:

  • Providing automated correction.
  • Providing simple but enough guideline, so we can write unit test to test rules to detect bug pattern.
  • It's product from Google so it has detailed documentation comment.
  • Using Annotation Processor to manage metadata of bug pattern, so we do not have to maintain XML files,

I haven't tried it with large amount of code, so I cannot compare its performance with Findbugs yet. I hope it's better than Findbugs. About openness and activeness in its community, I'm not sure yet. I've sent a pull-request to add bug pattern, I hope it's open even to non Google developers.

Do we need to switch to error-prone?

In my understanding, problem in error-prone is as follows:

So if you're using Findbugs, I think you do not have to switch to error-prone for now, its merit is really limited and almost all of them are not for users but for contributors.

But if you're author of Findbugs plugin, I think error-prone has value to keep watching on it. If its agility and resource is enough, it might grow and replace other solution later.

*1:I've sent some patches and pulll-requests but it's not enough yet

エラーログと例外の扱いについて

社内勉強会向けに作成したエラーログと例外の扱いについて説明したスライドを、Speakerdeckで公開しました。一部社内特化部分(ログレベルポリシーやMSA向けMDC使用法 など)を削除して、口頭で補足した部分を補っています。

これらの理想的な使い方はシステムを設計から運用までひととおり経験してみないとわかりにくいものですが、それなりの規模のシステム企業における業務開発でその経験を得ることはかなり難しいのではないでしょうか。結果、何の意味もなさないログが量産されてシステム運用の邪魔になってしまいます。きちんとポリシーを組んで統一していくか、各開発者が運用を知り納得して良いログを書けるようにしていく必要があります。

マネジメントとは挑戦と成長のための環境づくりだと考えている私にとって、ポリシー策定よりは「きちんと運用を知り自発的に支えになれる開発者」になれるための情報伝達こそがManagerとしてやるべきだと考えており、そのためのアクションがこのスライドというわけです。前回もそうでしたが、しばらくは非機能要件について考えるきっかけと大まかな方向性を提示していければと思っています。

関連する投稿

docker-composeで開発環境を配布する

上海Microsoftで行われたShanghai Docker MeetupのLTにて発表した、docker-composeを使って開発環境をContributorに対して配布するというユースケースに関するスライドをSpeaker Deckに公開しました。

Partake開発の際はVagrantで開発環境を配布していたのですが、今ならdocker-composeの方がシンプルかつ幅広く使われているのではと思います*1。中国からだとToolboxのダウンロードやDocker registryからのダウンロードが安定しないという問題があるのですが、中国Microsoftでミラー等の公開を検討しているそうです。

なおこのスライドを作るときにdocker-compose.ymlフォーマットのバージョン2が公開されている事に気づいたのですが、残念ながらnginx-proxyが未対応だったのでまだ自分のプロダクトでは使えていません。


追記:nginx-proxyがdocker-compose.ymlフォーマットのバージョン2に対応したそうです。

*1:VagrantからDockerを使う必要性はあまり感じていません

コード内コメントとJavadocの書き方

社内勉強会向けに作成したコード内コメントとJavadocの書き方についてのスライドを、Speaker DeckとSliDeckで公開しました。

このスライドは概要とメリットについてざっくり説明し理解と学習の動機を作ることを目的としていますが、これは日本ないし中国の大学ではコメントについてそこまで扱わないらしい*1という前提に基づいたものです。

ここに記載のない発展的な内容としては以下が挙げられますが、これらについて学ぶのはまず手を動かした後で良いと考えています。

プレゼン後に「publicでないクラスやメソッドJavadocを書くべきか?」という質問を受けましたが、私の答えは「書くべき」です。自分自身の理解を深め、備忘にもなり、将来のメンテナンスも助けてくれます。少しでも直感的でないコードがあるならば、時間を取って書きたいです。 まぁ書くべきものは厳密には対外的なAPI Specではなく、内部実装方針や歴史的経緯等になるかもしれませんので、Javadocを書くべきというよりは開発者向けドキュメントを書くべきと言うべきなのかもしれません。

また、ドキュメンテーションコメントはOSS contributionの良い題材になるとも話しました。typo修正くらいなら敷居は低いですし、多くのOSSでは喜ばれるはずです。自分の経験上、リジェクトされることはほとんどありませんでした:

なお自分のJavadocに関する理解は、Oracle公式ドキュメントに加えて「エンジニアのためのJavadoc再入門講座」に依るところが多いです。英語の解説書でこのくらい幅広く丁寧に説明したものが欲しい……。

エンジニアのためのJavadoc再入門講座 現場で使えるAPI仕様書の作り方

エンジニアのためのJavadoc再入門講座 現場で使えるAPI仕様書の作り方

*1:共同開発ないし保守については優先度が低い印象を受ける

(メモ)クラスタ管理関連

  • etcd
    • KVS
    • Leader/Follower
    • Raft
  • fleet
    • Deploy
    • Service Discovery
  • Hashicorp Consul
    • CP
    • needs several nodes as server per Datacenter
    • each nodes in Datacenter installs Consul agent
  • Hashicorp Serf
    • AP
    • is used by Consul
  • Docker Compose
    • Define set of container instances
  • Docker Swarm
  • Docker Machine
    • Create and initialise instance to deploy Docker container
    • Supports both remote and local
  • Registrator
    • Hook for docker command, to register/unregister container instances
  • Netflix Eureka
    • Service Discovery
  • SkyDNS2
    • CP
    • DNS but not only address but also ports
    • Depends on etcd
  • Hashicorp Terraform
    • Deployment
  • Hashicorp Otto
  • Hashicorp Packer
  • Netflix Asgard
  • systemd
  • Sensu

Guava Release19がそろそろ来そう

現時点でRC2まで出ています。

個人的に興味深いのはCharMatcherにファクトリメソッドが導入された理由。staticフィールドだとクラス初期化時に初期化されなければならないが、ファクトリメソッドならメソッド実行時までインスタンス生成を遅延できるためと説明されています。

// before
  public static final CharMatcher WHITESPACE =
      new NamedFastMatcher("WHITESPACE") {
          :
          :
          :
      };
// after
  public static CharMatcher whitespace() {
    return Whitespace.INSTANCE;
  }

  @VisibleForTesting
  static final class Whitespace extends NamedFastMatcher {
    static final Whitespace INSTANCE = new Whitespace();
    :
    :
    :
  }

なおこうした内部クラスを利用する方法*1はマルチスレッド環境でもsynchronizedや明示的なロックが必要ないことで知られています*2。 Stringでもprimitive型でもない定数、特に正規表現のような初期化処理が重いものは、積極的にファクトリメソッドで置き換えると良さそうです。

*1:このページのlazy initialization holder class idiom

*2:だからこういう問題も起こるのですが

SLF4Jの使い方をfindbugsで自動検証する

今日、拙作findbugs-slf4jGitHub Pageを作りました。このブログではこのプロジェクトについて日本語で紹介したいと思います。

概要

  • 拙作PMDプラグインをもとに、2012年8月から実装を開始したfindbugsプラグイン
  • SLF4J利用時のよくあるトラブルを未然に防ぎ、コードの品質を担保する
  • 全自動なので、開発者各々の個性に影響されずに品質の底上を実現できる

既存の課題:SLF4Jの直感的ではないインタフェース

SLF4J(Simple Logging Facade for Java)はJava向けに開発された、ログ用のFacadeです。開発者はSLF4Jが提供するインタフェースを利用することで、「高パフォーマンスなログ処理」と「利用者に好きなログ基盤を選ばせる自由」を提供することができます。例えばLog4j 2のような新しいログ基盤でも、プログラムがSLF4Jを使っていれば、プログラムには手を加えること無く利用が可能です。

ただしSLF4Jのインタフェースはやや独特で、学習と慣れが必要です。例えば下記に挙げる例ではexample 1のほうが直感的ですが、SLF4Jではexample 2の方を推奨しています。この方法はパフォーマンス上有利なのですが、コードに長年手が加えられていくとプレースホルダとパラメータ数の不一致などが容易に発生するため、ログに残すべき値が残せていなかったなどのトラブルに繋がります。

// example 1: not good, because JVM will concat strings even if debug log is needless
logger.debug("User id is: " + userId);

// example 2: better, because it will not generate needless string if debug log is needless
logger.debug("User id is: {}", userId);

また例外のstacktraceを出力するためのメソッドinfo(String format, Object... arguments)だというのもわかりにくい点です。以下の例のように可変長配列の最後の要素がThrowableであればstacktraceを出力してくれるのですが、これはドキュメントを読むか人に聞くかしなければわからないでしょう。

// example 3: not good, because stacktrace and log messages are separated into two log records.
// And user cannot configure how to log stacktrace.
logger.warn("Exception ({}) occurred when userId is {}", e.getMessage(), userId);
e.printStacktrace();

// example 4: better, it's configurable and not separated into multiple log records.
logger.warn("Exception occurred when userId is {}", userId, e);

SLF4Jは現在のJava開発でデファクトスタンダードと言えるログ用ライブラリですが、このような難しさがあるため利用時は経験ある開発者からのサポートやレビューが欠かせません。ただし、アプリケーションコードのどこにでも書かれうるログ処理のすべてを人力でレビューし品質を担保するのは至難の業です。

提案する解決:静的解析による自動的かつ横断的な検証

私が開発したfindbugsプラグインの発想はごくシンプルなもので、上記で挙げた問題を機械による検証によって解決するというものです。 個人の経験と尽力によって回避するのではなく、検証を機械化し自動化することでコストの削減とスケーラビリティの向上、抜け漏れ防止を実現します。

技術的な特徴

主な技術としては、findbugsが提供するオペランドスタック解析を主に使用しています。これによりプレースホルダとパラメータの数の不一致を検出したり、e.getMessage()をパラメータとして使っているコードを検出したり、LoggerFactory.getLogger(Class)メソッドの引数を検証したりしています。また.classファイルに書かれた定数文字列を解析することで、簡易ではあるもののログフォーマットの人間にとっての可読性を検証するといったことも取り入れています。

findbugsプロジェクトは息が長いということもあり、コードが非常に難解です。またテストも困難です。 このプラグイン開発では統合テストを重視テストパターンを増やすことで、TDDを可能にしつつエンバグを減らすことに努めています。またバグ修正時はまず再現テストを書く、ということを徹底しています。

またTravis CIが提供する機能により、Java 7&8SLF4J 1.6&1.7の2軸でビルドマトリックスを組んでいます。これも多くの環境をサポートする上で非常に役立っています。

どのくらい使われているか

OSSプロジェクトでの利用実績はほとんど無いと認識していますが、クローズドなプロジェクトではそこそこ使われているようです。2012年11月にいただいたIssueをはじめとして、今でもバグ報告・新規機能要望などをいただきます。「このコードを解析するとエラーになる」と、.javaファイルや.classファイルをメールでいただくこともあります。

特にこの5月ごろから月あたりのDL数が平均40から1,700(ユニークIPアドレスベースでの計数)へと大幅に上がったのですが、どこかの大きなプロジェクトで使っていただいているのでしょうか。

f:id:eller:20150920134539p:plain

以上です。 もし皆さんのプロジェクトでSLF4Jを使っていたら、ぜひ当findbugsプラグインの利用を検討してみてください。Mavenで使う方法はこちらに書いてあります

関連する記事