負債の墓場を築かない:ESLintカスタムルールによる「非推奨APIの追放」とCI自動化の深淵
技術的負債とは、コードが書かれた瞬間に発生する「利子」ではない。「誰も使ってはならないと知りながら放置されたAPIが、静かにコードベースを蝕み続ける時間」のことだ。
多くのチームは、ドキュメントやREADMEで「このモジュールは非推奨です」と警告する。しかし、そんなものは誰も読まない。コードレビューで指摘するのも非効率だ。伝説的なDevOpsアーキテクトである我々が目指すべきは、「間違ったコードを書いた瞬間、その行が赤く染まり、CIが拒絶する」という、物理法則レベルの強制力を持つ仕組みである。
本稿では、ESLintを単なる静的解析ツールとしてではなく、組織の技術スタックを強制的に進化させる「ゲートキーパー」として運用する極意を伝授する。
—
1. 静的解析の境界線:なぜ `no-restricted-imports` では足りないのか
標準の `no-restricted-imports` は強力だが、あくまで「パッケージ単位」の禁止に留まる。しかし、現場で本当に必要なのは「特定のメソッド呼び出し」や「古いバージョンの関数シグネチャ」の追放だ。
ここで登場するのが、AST(抽象構文樹)を直接操作するESLintカスタムルールである。我々は、`eslint-plugin-local-rules` を活用し、リポジトリ内に独自のルールを直接埋め込む。
カスタムルールの実装アーキテクチャ
ルール定義は、ESTree specに基づいたパターンマッチングを行う。以下は、レガシーな `legacyClient.request()` を禁止し、現代的な `fetch` への移行を強制する最小構成のルール例だ。
// eslint-rules/no-legacy-api.js
module.exports = {
create(context) {
return {
// CallExpression(関数呼び出し)をフックする
CallExpression(node) {
// オブジェクトが ‘legacyClient’ かつ メソッドが ‘request’ かを検証
if (
node.callee.type === ‘MemberExpression’ &&
node.callee.object.name === ‘legacyClient’ &&
node.callee.property.name === ‘request’
) {
context.report({
node,
message: ‘🚨 警告: legacyClient.request は非推奨です。fetch API に移行してください。’,
});
}
},
};
},
};
このルールを `eslint-plugin-local-rules` を介して `.eslintrc.js` に登録することで、CI/CDパイプラインの深層部で、開発者のエディタ上でも即座に違反が検出されるようになる。
—
2. CI/CDパイプラインへの「拒絶」の実装
ルールを書いて終わりではない。最も重要なのは、「警告を放置させない」CI/CDフローの設計だ。多くのチームが `eslint –fix` を自動化しているが、これは諸刃の剣である。
パイプライン最適化のハック
CI環境(GitHub Actionsなど)では、メモリ消費を抑えつつ、差分のみをチェックする戦略をとる必要がある。`lint-staged` をローカルに仕込み、コミット単位での防壁を構築せよ。
.github/workflows/lint.yml
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
cache: ‘npm’ # キャッシュ効率を最大化
- run: npm ci
# –max-warnings 0 を付与し、警告一つでもビルドを落とす
- run: npx eslint . –max-warnings 0
ここで重要なのは、「警告をエラーとして扱う」という厳格なポリシーだ。警告が放置される環境では、静的解析はノイズとなり、エンジニアはそれを無視するようになる。
—
3. 大規模リポジトリにおける「負債返済」の自動化戦略
ルールを導入した瞬間に、既存の数千行のコードがエラーで埋め尽くされるはずだ。これを一気に直すのは非現実的である。ここで、「新規ファイルは厳格に、既存コードは徐々に」という戦略をとる。
段階的な移行を支えるスクリプト
ESLintのルールを「警告」から「エラー」に切り替える際、既存の違反を一時的に無視する `eslint-disable` を自動挿入するスクリプトを走らせるのが定石だ。
ASTを解析し、特定のルール違反箇所にコメントを自動挿入するワンライナーの概念
実際には eslint-find-rules や jscodeshift を用いるのが賢明である
npx jscodeshift -t transform-add-eslint-disable.js src/
このアプローチにより、開発者は「新しい機能を作る際に、負債を積み増さない」ことを強制されつつ、既存コードの改修は「自分のタスクのついでに」という効率的なプロセスで進められるようになる。
—
4. アーキテクトの視点:パフォーマンスとメモリ消費の最適化
ESLintは強力だが、AST生成はメモリを大量に消費する。数万ファイル規模のプロジェクトでは、Lintの並列実行設定は必須だ。
- キャッシュの永続化: `eslint –cache` を必ず利用すること。`.eslintcache` ファイルをCIのキャッシュストレージに保存することで、変更のないファイルの再解析を完全にスキップできる。
- プロファイリング: `TIMING=1 eslint .` を実行せよ。どのカスタムルールがボトルネックになっているか、ミリ秒単位で可視化される。コストの高いルールは、特定のディレクトリに限定するなどして最適化せよ。
—
結論:ツールは文化を強制する
ESLintのカスタムルール作成は、単なるコードチェックではない。それは、「チームがどのような技術スタックを志向すべきか」という設計思想をコードとして具現化する作業である。
「古いAPIを使うな」と口頭で伝えるのはマネジメントだが、「古いAPIを使えないようにシステムを組む」のがエンジニアリングだ。この強固な自動化の輪を回し続けることで、あなたのチームは「過去の遺物」を修正する時間を減らし、「未来の価値」を創造する時間を最大化できる。
さあ、コードベースから「負債」という名の贅肉を削ぎ落とせ。あなたの書くルール一つが、組織の生産性を数年分前倒しにするのだから。