Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

eslint-loader の使用をやめることで、ビルド時間を短縮した話

こんにちは。 Eight 事業部でエンジニアをしている鳥山(@pvcresin)です。
最近は、持ち手部分に適度な厚みと重さがあるいい感じのカトラリー(食卓用のナイフ・フォーク等)を探すのにハマっています。 おすすめのものがある方はご一報いただければ幸いです。
さて今回は、webpack 用の loader である eslint-loader の使用をやめることで、ビルド時間を短縮した話をしたいと思います。

はじめに

ビルド時間の短縮は、開発者体験の向上において最も大きなトピックの一つです。
ビルド時間を短縮することで、その後の開発スピードの向上、ひいてはプロダクト全体の価値提供速度の向上につながります。
私はビルド時間の短縮に関して行えることはないか常日頃から考えており、今回はその中で見つけた不要な処理を削除したという話になります。

eslint-loader

Eight の Web フロントエンドは JavaScript と TypeScript で書かれています。 そして、ビルドには webpack を、Lint(静的解析)には ESLint を使用しています。
今回言及する eslint-loader は、 webpack のビルドプロセス内で入力されたファイルに対して ESLint を走らせることができるツール(loader)です。
これにより、ビルド中に Lint エラーが見つかった場合にビルドの処理を止めることができます。 また、webpack-dev-server での開発中には、ファイル保存の度に Lint を走らせることができます。

ちなみに後継の eslint-webpack-plugin の登場で、 eslint-loader は 2020 年 9 月に非推奨になっており、GitHub のリポジトリもアーカイブ状態になっています。

導入の経緯

過去の PR(プルリクエスト)を確認したところ、eslint-loader は 2017 年 11 月(約 3 年前)に Eight に導入されていました。
当時、ESLint が CI でしか実行されておらず、Lint エラーに気付くまでに時間がかかるという問題がありました。 当時からエンジニアは webpack-dev-server による開発を行っていたため、 eslint-loader の導入によって、ファイル保存の度に Lint エラーを即座に見つけることができるようになりました。
これによって Lint エラー修正までの時間は短縮され、開発者体験は向上しました。

ビルド時間の短縮

では、ビルド時間の短縮という観点ではどうでしょうか。
ビルド時間の短縮にはいくつか方法がありますが、その中の一つに「処理を減らす」ことが挙げられます。 eslint-loader は入力されたファイルの Lint エラーをチェックするものであり、処理を止めることはあっても、ビルドという点においては何も行っていません。 その点を踏まえると、eslint-loader を削除しても良いのではないかと考えました。

現在の状況と比較

次に導入当時と現在の状況を比較しました。
結論としては以下の理由から、eslint-loader の使用をやめても良いのではないかと考えました。

  1. ファイルのコミット時に ESLint を実行している
  2. エディタに ESLint のエラーが表示される
  3. ビルドと Lint の責務はわけるべき

1. ファイルのコミット時に ESLint を実行している

以前、Prettier を導入した際 に Git のフックを利用して、 任意の処理をローカル環境で実行できる仕組み (Husky + lint-staged)を導入しました。
これにより、ファイルのコミット時に ESLint によるチェックが行われます。 したがって、webpack-dev-server を使った開発をしている場合に Lint が走るタイミングは、ファイル保存時 -> コミット時 -> CI の 3 回となっていました。
確かに eslint-loader を使った方がより早く Lint エラーに気付くことができます。 しかし、eslint-loader の使用をやめて、コミット時に初めて Lint を走らせる形になっても、十分ストレスなく開発が行えるのではないかと考えました。

2. エディタに ESLint のエラーが表示される

当時の状況はわかりませんが、現在ではエディタや IDE などがリポジトリ内の ESLint の設定ファイルを読み取り、 Lint エラーをリアルタイムにコード上に可視化してくれることも珍しくありません。
この点を踏まえると、eslint-loader が行っていたファイル保存時の Lint 実行と、実は体験にほとんど差は生まれないのではないかと考えました。

3. ビルドと Lint の責務はわけるべき

最後に、そもそも論としてビルドと Lint は別々にすべきではないかと思いました。
私の理解として、ビルドの主な責務はコード(やリソース)を実行可能な形式にすること1であり、 Lint の責務はその前段となるコードの品質の担保にあると考えています。 eslint-loader によって、それらの責務がひとまとまりになっているような感覚がありました。

以上 3 つの理由から、eslint-loader の削除に踏み切りました。

eslint-loader の削除

eslint-loader の削除は、Eight 全体のビルド環境に手を加えることを意味します。
そのため、他チームのフロントエンド開発を行っているエンジニアに事前に影響や作業方針について合意をとりました。 その上で、eslint-loader の削除作業を行いました。

作業内容

作業としては至ってシンプルです。 webpack.config.js の eslint-loader 使用箇所を消し、リポジトリからも eslint-loader を削除しました。

// webpack.config.js
module.exports = {
  /* 省略 */
  module: {
    rules: [
      /* 省略 */
-     {
-       test: /\.(js|jsx|ts|tsx)$/,
-       enforce: 'pre',
-       exclude: /node_modules/,
-       use: [
-         {
-           loader: 'eslint-loader',
-           options: {
-             // ...
-           },
-         },
-       ],
-     },
    ],
  },
};

効果

今回の目的はビルド時間を短縮することなので、作業の前後で実際に効果があったのか測定していきます。

ローカルでのビルド時間

まず、最もよく使う、ローカルでのビルド時間です。 測定には私のマシン2を使い、開発モードでのビルド時間(5 回平均)を測定しました。

Before After
ビルド時間 (秒) 53.57 44.20

結果、ビルド時間は 17.5 % 削減することができました。 しっかりビルド時間が短縮できて一安心です。

検証サーバへのデプロイ時間

次に検証サーバへの、ビルドを含むデプロイ完了までの時間(5 回平均)を測定しました。

Before After
デプロイ完了時間 (秒) 360.4 267.4

先のビルド時間の短縮にともない、デプロイ時間を 25.8 % 削減することができました。 QA を行うフェーズでは検証サーバにデプロイしてテストするため、こちらも嬉しい変化です。
デプロイ時間の削減に関しては当初あまり想定していませんでしたが、 ビルド時間の短縮の重要性を再認識する良い機会となりました。

予期せぬ効果

上記以外にも、予期せぬ効果がありました。 それは、コミット前の変更に対して Lint エラーを直さなくても良くなったことです。
これまでは、ファイルを保存する度に Lint チェックが走っていたので、作業中であっても常に Lint エラーが出ないようにコードを書く必要がありました。 そして、一時的に Lint エラーを回避したい場合には、eslint-disable コメントを付与して対処していました。
しかし、これからはコミット時に Lint エラーがない状態になっていれば良いので、作業中に Lint エラーを意識する必要がなくなりました。
この効果について測定することは難しいですが、メンバーからも開発時のストレスが軽減されたとの声があがっています。

まとめ

今回は、eslint-loader の使用をやめることで、ビルド時間を短縮した話をしました。
ビルド時間が短縮されたことで、開発がよりスムーズに進んでいる感覚があります。
ビルド時間の短縮についてはこれからも常に考えていこうと思います。
過去と現在とでは、状況が変わっているかもしれません。
時には立ち止まり、前提を再確認することを大切にしていきたいですね。


  1. もちろん他にもリンクをはじめとする、たくさんの責務があると思います

  2. MacBook Pro (15-inch, 2018), 2.9 GHz 6-Core Intel Core i9, RAM 32 GB

© Sansan, Inc.