レガシーコードを「破壊」せず「進化」させる:ESLint漸進的導入のアーキテクチャ
大規模なコードベースにESLintを導入する際、最も愚かな行為は、`–fix`を全ファイルに適用し、数千件のDiffを生成してGit履歴を汚染することだ。それは「コードの品質向上」ではなく「負債の爆発」である。
本稿では、既存の巨大なプロジェクトに対し、開発者の生産性を一切落とさず、かつCI/CDのゲートを強固にするための「戦略的ESLint導入」の核心を解説する。
—
1. 破壊的変更を回避する「二段階防御」戦略
導入初期において、既存のすべてのファイルをルールに適合させることは不可能だ。まずは「新規コードのみを厳格化し、既存コードは放置する」という境界線を明確にする必要がある。
Step 1: `.eslintignore` の逆転発想
まず、すべてのファイルを無視対象にする。
.eslintignore
すべてを無視し、明示的に含めたものだけを解析対象にする
!src//.ts
!src//.tsx
Step 2: `eslint-nibble` ではなく「段階的適応」
多くのエンジニアは`eslint-nibble`で修正を試みるが、大規模チームでは制御不能になる。私が推奨するのは、`eslint-config-airbnb`等の厳格なベースではなく、「まずはWarnのみ」の構成から始めることだ。
`eslintrc.js` の `rules` を以下のように設計する。
module.exports = {
// …
rules: {
// 既存コードを壊さないため、まずはすべて ‘warn’ に倒す
‘no-console’: ‘warn’,
‘@typescript-eslint/no-explicit-any’: ‘warn’,
// 導入フェーズが完了するまでエラーは出さない
}
}
—
2. CI/CDにおける「負債の固定化」テクニック
既存のLintエラーを無視しつつ、新規エラーの混入だけを防ぐには、ESLintの `cache` と `–report-unused-disable-directives` を活用する。
CIパイプラインの最適化コマンド
単に `eslint .` を叩くのはアーキテクトの仕事ではない。メモリ消費と実行時間を最小化する以下のコマンドをパイプラインに組み込め。
–cache: 変更のあったファイルのみを解析(.eslintcacheに状態保持)
–max-warnings 0: warnをエラーとしてCIを失敗させる(段階的引き締め)
–report-unused-disable-directives: 不要になった eslint-disable を炙り出す
eslint –cache –cache-strategy content –report-unused-disable-directives .
なぜこれが重要か:
`–report-unused-disable-directives` を入れることで、コードのリファクタリングにより不要になった抑制コメントを即座に特定できる。これにより、負債が放置されずに「自然治癒」するサイクルが生まれる。
—
3. メモリと速度の限界突破:Worker Poolの活用
ESLintはデフォルトではシングルプロセスで動作する。数万ファイル規模のプロジェクトでは、Lintだけで数分を要するのはザラだ。これを回避するため、`eslint-webpack-plugin` や `lint-staged` を超えた、Node.jsのワーカースレッド最適化を検討すべきだ。
もしCLI実行の遅延が許容できないなら、`–parallel` オプションを持つラップスクリプトを自作する。
// lint-parallel.js
const { Worker } = require(‘worker_threads’);
// ファイルリストを分割し、OSの物理コア数に合わせて並列実行する
// 実行時間が線形的に短縮される
—
4. 現場で震えるほど役立つ「自動修正」の極意
`eslint –fix` を実行すると、フォーマッタ(Prettier)と競合して無限ループに陥ることがある。これを防ぐための決定版構成は、「Prettierは整形のみ、ESLintは構文チェックのみ」という責務分離だ。
`eslintrc.js` に以下のプラグインをマージせよ。
module.exports = {
extends: [
‘plugin:@typescript-eslint/recommended’,
‘prettier’ // ESLintのルールとPrettierが競合する設定をすべてOFFにする
],
// 修正の自動化には –fix ではなく、husky + lint-staged を推奨
// ただし、コミットフックは遅延の原因になるため、CI側で「差分のみ」をチェックするのが正解
}
—
5. アーキテクトからの提言:データドリブンな品質管理
最後に、導入の成否を測るために、Lintエラーの推移を可視化せよ。
1. JSON出力: `eslint –format json –output-file report.json` で結果を吐き出す。
2. 蓄積: パイプラインのアーティファクトとして保存する。
3. 分析: どのルールが最も違反されているか(Top 10)を定期的に抽出する。
もし特定のルール(例:`no-any`)が改善されないのであれば、それは個人の意識の問題ではなく、アーキテクチャ上の欠陥である可能性が高い。ツールは「コードを直す」だけでなく、「チームのボトルネックを特定する」ために使うものだ。
この戦略を徹底すれば、大規模レガシープロジェクトであっても、3ヶ月後には「かつては無法地帯だったコード」が、強固な型安全と一貫したスタイルを備えた「資産」へと変貌しているはずだ。
さあ、コマンドを叩く前に、まずはこの設計思想をチーム全員で共有することから始めてほしい。真のDevOpsとは、コードではなく人間とプロセスの間に介在する摩擦を減らすことなのだから。