Lintと実行タイミングの話

こんにちは。Lint Advent Calendar の1日目の記事になります。
少し遅れましたが、UTCではまだ12/1なので許して下さい。

TL;DR
=========


|                      Lintの実行タイミング          | メリット | デメリット
|---------------------------------|---------|----|
|ファイル保存時(エディタ連携、guard等)  | バグの早期発見  | 開発者への周知が必要
| git commit前(hook、手動等) |  そこそこ早期発見、そこそこ設定が楽  | pre-commit hookの設定が必要、遅いとつらい |
| CI上での実行(CircleCI、SideCI等)              | 全てのコードに必ずLintを書けられる | 早期発見は出来ない



はじめに
==========

少し前に、Twitterでこんなアンケートを取りました。

> アンケート
> RuboCopやESLintなどのLinterを、次のうちどのタイミングで実行していますか?
> 複数当てはまる場合は、一番早いタイミングで実行されるものを答えて頂けるとありがたいです。
> - ファイル保存時(エディタ連携、guard等)
> - git commit前(hook、手動等)
> - CI上での実行(CircleCI、SideCI等)
> - 実行しない/その他(リプライ頂けると嬉しいです)
> 
> https://twitter.com/p_ck_/status/777448862101348353

その結果、67件の解答をいただき次のような結果が得られました。

|                      Lintの実行タイミング          | 割合 |
|---------------------------------|---------|
|ファイル保存時(エディタ連携、guard等)  | 54%  |
| git commit前(hook、手動等) | 24%   |
| CI上での実行(CircleCI、SideCI等)              | 13%  |
| 実行しない/その他(リプライ頂けると嬉しいです)          | 9%  |

また、その他の解答として「コード入力時」というものが1件得られました。


今回は、このアンケートにあるLintを実行する各タイミングについて考察したいと思います。


1.ファイル保存時(エディタ連携、guard等) && コード入力時
====================

これが一番早いタイミングでの実行になると思います。



メリット
----------

この段階でLintを実行することには、「バグの原因をいち早く見つけられる」というメリットがあります。

当然のことではありますが、早いタイミングでのLintの実行になるため、早いタイミングでバグを見つけることが可能となります。
これは手軽に実行できるLintの特性を最大限に活かした使い方だと言えます。

これにより、実行時に判明するような問題や特定のケースでしか起こり得ない問題を早期に発見し、手戻りを少なくコードを改善していくことが可能となります。

この点に関しては(私の尊敬するエンジニアである)Kuniwak氏がとても良いスライドを公開されています。
[慣れる？JS 素早く究明！デバッグ入門 // Speaker Deck](https://speakerdeck.com/orgachem/guan-reru-js-su-zao-kujiu-ming-debatuguru-men)

また、いち早く実行することで、有効に扱えるルールの幅が広がります。
例えば、実行時に発生するであろう問題の検査は、動作確認やテストでも問題が発見出来る可能性があるため、それらを行うより前である編集中の段階で検査することに大きな意味があります。
少しLintとは話が逸れますが、gofmtやRuboCopのStyleルールのAuto-Correct、ESLintの`--fix`などのコードのフォーマッティング機能も編集中だからこそ活きる機能でしょう。


上記のような理由から、編集中にLintを使うことは非常に有意義だと考えられます。


デメリット
--------

ですが、このタイミングでの実行にもデメリットがあります。
開発者全員がLintを使用してくれるとは限らない、ということです。

編集中にLintを実行するには、当然個々の開発者が使用するエディタを自前で設定する必要があります。
そのため、人によってはLintを動かしていなかったり、警告をあまり見なかったり、など開発者間でのブレが生じてしまうでしょう。

また、動作があまりにも遅いツールの場合、あまり有効に機能しないことも考えられます。
回避方法としては、例えば[alecthomas/gometalinter](https://github.com/alecthomas/gometalinter)の場合は`--fast`という、速いツールのみを実行するオプションが存在するため、それを付与することで回避することが出来ます。
また、ツールによってはLintのプロセスをデーモン化することで、初期化コストを抑えて高速化することが出来るものもあるようです。



2. git commit前(hook、手動等)
=========================

これは2番目に早いタイミングになると思います。
gitのpre-commit hookを使用して、コミット前にLintを走らせることが出来ます。

メリット
-------

この方法のメリットとしては、以下のものが上げられます。

- 編集中ほどではないが、そこそこ早いタイミングでLintが実行できる
- gitのpre-commit hookであれば、ファイルをcpするだけでいいのでエディタの設定よりは楽
- Lintに違反するコードをpushさせない、といったことが可能


デメリット
-----

エディタの設定よりは楽ですが、pre-commit hookを設定しないといけないため、個々のエンジニアの作業は依然として存在します。
また、pre-commit hookはgit pullによって自動的に更新されないため、hookのスクリプトを更新した場合は手動でアップデートする必要があります。

そしてLintの実行速度も問題となってきます。
編集中であればLintを非同期に実行できるのですが、コミット前だと同期的に実行する必要があるため少し待たされてしまいます。
そのため、起動が遅いツール程コミットがストレスとなってしまいます。



以上のことから、このタイミングは、コードの編集時と、後述するCIでの実行の折衷案とも言えると思います。



3. CI上での実行(CircleCI、SideCI等)
===================

このタイミングは、CircleCIやSideCIなどのCIを使った実行です。
上げたタイミングの中では、一番遅いものになります。

メリット
-------

この方法の一番のメリットは、全開発者のコードにLintがかかることだと考えています。
個々の開発者の設定が要らないため、どのコードにも平等にLintをかけることが出来ます。
これにより、開発者間の共通のルールとしてLintを扱いやすくなるでしょう。

また、[SideCI](https://sideci.com)や[haya14busa/reviewdog](https://github.com/haya14busa/reviewdog)などのサービス/ツールを使用することで、GitHub Pull Requestでコードレビューを行ういつものプロセスで、Lintの実行を行うことが可能です。
そのため、Lintが根付いていないチームにLintを導入することの障壁が比較的少ないと考えられます。


また、CIの実行は普段の開発とは非同期に行われるため、動作が遅いツールの実行時間も許容しやすくなるでしょう。


デメリット
------

1つ目の項でも述べたとおり、問題の早期発見からは少し遅れてしまうことになります。
そのため、編集中でのLintの実行で享受できるようなメリットは、CIでの実行ではあまり効果が出ないでしょう。



まとめ
=========


今まで上げたメリット/デメリットから、

- 編集中(or commit前)のLintの実行でバグの早期発見
- セーフティーネット、Lint導入の為のCIでの実行

の2つを併用するのがLintとのよい付き合い方だと考えています。