依存関係の「幽霊」を狩る:CI/CDを「真実のソース」へと進化させる極致
開発現場において、`node_modules`はしばしば「ブラックボックス」と化す。`package.json`には記載されていないのに、なぜか`import`できてしまう「幽霊依存(Phantom Dependencies)」。あるいは、プロジェクトの肥大化を招く「ゾンビ依存(Unused Dependencies)」。これらは単なる技術的負債ではない。ビルドの非決定性、セキュリティパッチの漏れ、そしてCIキャッシュの無駄な肥大化という、プロダクトの寿命を削る癌である。
本稿では、npm/pnpmのエコシステムにおいて、これらをCIパイプラインで完全自動的に検閲・駆逐し、リポジトリを常に「クリーンな状態」に保つための、アーキテクト級の戦略を伝授する。
—
1. 幽霊依存のメカニズム:なぜ「見えてしまう」のか
Node.jsのモジュール解決アルゴリズム(Node Resolution Algorithm)は、物理的な`node_modules`ツリーを再帰的に走査する。この際、ホイスティング(Hoisting)によって、本来依存していないはずのパッケージが上位ディレクトリに展開されると、コード上からそのパッケージをrequire/importできてしまう。これが幽霊依存の正体だ。
特にpnpmの「厳格な依存関係解決」を導入している場合、この境界線は明確だが、依然として`package.json`の記載漏れ(ビルド時のみ必要なパッケージのミス等)は発生しうる。これを静的解析で狩り取るのが、`depcheck`の責務である。
—
2. depcheckとsyncpackによる「検閲パイプライン」の構築
単に`depcheck`を走らせるだけでは不十分だ。我々が構築すべきは、「依存関係の異常を検知した瞬間にビルドを中断し、原因を特定可能な形でログを吐き出す」検閲官である。
CI工程への統合(GitHub Actions設定例)
jobs:
dependency-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with: { node-version: ’20’ }
# 1. 幽霊とゾンビを狩る
- name: Depcheck Audit
run: |
npx depcheck –json > depcheck-report.json
# 終了コード0以外を検出してパイプラインを止める
# –ignore-bin-package等、プロジェクトごとのカスタマイズが肝
npx depcheck –ignores=”eslint-config-,@types/”
# 2. バージョンの不整合を統一する
- name: Syncpack Audit
run: npx syncpack list-mismatches
アーキテクトの知見:depcheckのカスタマイズ
`depcheck`の標準設定では、動的インポートや特定のビルドツール(Vite/Webpack)のプラグインを検知できないケースがある。その際は、`.depcheckrc`をプロジェクトルートに配置し、解析対象外を指定するのではなく、「パーサーを拡張する」アプローチをとる。
// .depcheckrc
{
“ignores”: [“eslint-“, “prettier-“], // Lint/Format系はビルドに影響しない
“parsers”: {
“/.js”: “es6”,
“/.ts”: “typescript”
},
“detectors”: [
“requireCallExpression”,
“importDeclaration”
]
}
—
3. syncpackによるモノレポ管理の「真の自動化」
モノレポ環境では、`package.json`間でのバージョン不一致は「悪」である。`syncpack`は単なるバージョン統一ツールではない。依存関係のグラフを正規化する強力なエンジンだ。
CIで実行すべきは、単なる修正ではない。「強制的な修正」と「プルリクエストへのフィードバック」である。
現場で震えるほど役立つ、syncpackのCI実行コマンド
1. 不整合があればエラーを吐いて終了
npx syncpack list-mismatches –filter ‘!@internal/’
2. 自動修正をCI上でコミットしてプッシュするフロー(推奨)
修正が必要な場合、syncpack fix を実行し、変更をコミットしてPRに追記するオートメーションを組む
npx syncpack fix-mismatches
—
4. 低レイヤ最適化:メモリ消費とパフォーマンスのハック
数千のモジュールを持つ巨大リポジトリで、`depcheck`をCIで回すとメモリが枯渇することがある。この場合、Nodeのメモリ制限を明示的に引き上げるのが鉄則だ。
CI環境での実行コマンド最適化
NODE_OPTIONS=”–max-old-space-size=4096″ npx depcheck
また、Docker上でこれを行う際は、`node_modules`のキャッシュ戦略が重要だ。GitHub Actionsのキャッシュにおいて、`pnpm-store`と`node_modules`を分離し、`pnpm store prune`を定期実行することで、ビルド時間を数分単位で短縮できる。
—
5. 結論:依存関係を「設計」するということ
依存関係管理を「インストールする作業」から「パイプラインで自動検閲するアーキテクチャ」へ昇華させることは、エンジニアリング組織の成熟度を測るリトマス試験紙である。
1. depcheckで「不要なもの」を捨て、
2. syncpackで「不整合なもの」を正し、
3. CIパイプラインで「それらを法として強制する」。
このサイクルを確立した瞬間、あなたのチームは「なぜか動かないビルド」や「謎の幽霊パッケージ」に時間を奪われることから永久に解放される。次は、これらを`Husky`によるプリコミットフックに組み込み、開発者のローカル環境から「健全な状態」を強制するステージへ進むことを推奨する。
技術はただ使うものではない。プロジェクトの守護神として、コードベースの深淵に鎮座させるものだ。健闘を祈る。