dependency-hygiene について
dependency-hygieneは、Flutter/Dartアプリ開発向けの依存関係管理スキルです。ロックファイル、依存監査、更新、ベンダリングの手順を定め、推移的依存関係も確認します。
主な機能
- 01依存範囲とロックファイルの規則
- 02推移的依存関係の監査
- 03SDKとlint includeの確認
- 04更新と実機検証の指針
- 05インターフェース経由のプラグインベンダリング指針
活用シーン
- 01pubspec.yamlの変更レビュー
- 02新しいパッケージ導入前の監査
- 03SDKやネイティブプラグインの更新
- 04依存関係の削除やベンダリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zakariaf/ flutter-skills --skill dependency-hygieneインストール後、Claude Code で/dependency-hygieneを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/vendored_plugin_behind_interface.dartreferences/dependency-gate-and-audit.mdsdk-pin-and-lint-include.mdvendoring-behind-an-interface.mdscripts/audit_deps.pyaudit-deps.sh詳細
依存関係の衛生管理
依存関係はどれも、いずれ誰かが保守しなければならない負債です。「最新」ではなく、「何年後も解決してビルドできること」を優先してください。このスキルでは、pubspec と lock の仕組み、新しいパッケージが通過すべきゲート、重要なパッケージが劣化した場合の回避策を扱います。
作業内容に応じて、次のリファレンスを読んでください。
references/dependency-gate-and-audit.md— 拒否/受け入れのゲート、推移的依存関係の監査、ライセンスの記録、アップグレードの規律。references/sdk-pin-and-lint-include.md— SDK バージョンの記録と、SDK 更新時に発生する気づきにくい lint 無効化の落とし穴。references/vendoring-behind-an-interface.md—lib/に手を加えず、バス係数が 1 のネイティブプラグインをthird_party/にベンダーする方法。
pubspec.yaml を変更する PR の前に、scripts/audit-deps.sh を実行してください。
絶対に守るルール
pubspec.yamlではキャレット範囲を使い、正確な固定はpubspec.lockのみで行います。drift: ^2.31.0とし、drift: 2.31.0とはしません。lock はバージョンを固定し、範囲は解決可能な状態を保つためだけに使います。pubspec で正確なバージョンを固定すると、次回の SDK 更新時に解決不能な競合を生みますが、lock がすでに実現している以上の利点はありません。pubspec.lockをコミットします。 これはパッケージではなくアプリケーションです。その違いによってルールが逆になります。Dart のデフォルトの.gitignoreテンプレートにはpubspec.lockが記載されているため、その行を削除してください。コミット済みの lock だけが、実機でテストされた正確なバージョンを、他の開発者がgit cloneして解決できるようにします。pubspec.yamlの差分に対応するpubspec.lockの差分がなければ、不完全です。flutter pub getを実行し、追加、アップグレード、削除のいずれの場合も、同じコミットに lock を含めてください。- SDK のバージョンはツールとは別に記録します。
pubが依存関係を解決できるように、environment: sdk:は実際の範囲指定にします。テスト済みの正確な Flutter バージョンは、CI が参照するコミット済みの記録に固定してください(リファレンスを参照)。他の開発者と CI が読むのはその記録です。バージョン管理ツールとファイルを混同しないでください。 - lint の include ファイル名は、解決された SDK と連動しています。 バージョン固定された
include:が、解決済みの linter パッケージに存在しないファイルを指定すると、include_file_not_foundが出力されます。これはデフォルトのdart analyze/flutter analyzeでは致命的なエラーとなるため、標準の CI で検出されます。警告を致命的でない扱いにしている場合に限り、何も知らせずに成功します。その場合でも適用されなくなるのは、analyzer の組み込みルールではなく、ルールセットで追加または昇格したルールです。SDK を更新するたびに確認してください(リファレンスを参照)。 - パッケージを追加する前に、pubspec ではなく推移的依存関係のツリーを監査します。 禁止対象の SDK が入り込むのは、まさに 2 段目の依存関係です。新しい依存関係をコミットする前に、
scripts/audit-deps.sh(またはdart pub deps --jsonと監査スクリプトの組み合わせ)を実行してください。 - ネットワークへの接続経路を開く、クラッシュや使用状況を報告する、テレメトリのコアを引き込む、または未リリースの機能のためにデバイス識別子を収集するものは、直接依存でも推移的依存でも、ポリシーにより拒否します。 CI が成功しても、ネイティブ機能が動作する証拠にはなりません。ゲートのリファレンスを参照してください。
- 依存関係を削除した後は
lib/を grep します。 パッケージをpubspec.yamlから削除しても、推移的に解決可能なままだと、今日のビルドは通りますが、重要な clone でビルドが壊れます。analyzer が検出するのは解決されない import であり、偶然解決されている import ではありません。 - バス係数が 1 のネイティブプラグインは、今すぐインターフェースの背後に置き、ベンダーするのは後にします。 インターフェースは安価な保険です。まだ発生していない破損に備えて先回りしてベンダーするのは、保守の負担になります。ベンダーのリファレンスを参照してください。
pubspec では範囲を指定し、lock では固定する
| すること | 絶対にしないこと |
|---|---|
キャレット範囲:drift: ^2.31.0 | 正確な固定:drift: 2.31.0 |
environment: sdk: ^3.6.0 — 実際の範囲指定 | sdk: の正確なバージョン指定 |
pubspec.lock をコミットする | pubspec.lock を Gitignore に追加する |
lock が最優先の情報源です。範囲指定は SDK アップグレード時にソルバーが更新できる状態を保ち、lock は実際に検証されたバージョンを全員が使えるようにします。
新しい依存関係が必ず通過しなければならない関門
パッケージ自体、または推移的な依存関係ツリー内の何かが次のいずれかを行う場合、そのパッケージは受け入れません。
- 製品がネットワークを使用しない、またはオフラインファーストを約束しているのに、自分で選んでいないネットワーク経路(
http、dio、ソケット、gRPC)を開く。 - テレメトリーを行わない方針に反して、クラッシュや使用状況を報告する(Crashlytics、Sentry、あらゆる分析またはアトリビューション SDK)。
- テレメトリー/分析のコアを推移的に取り込む。クラッシュ SDK のコアは、呼び出していなくてもストアのプライバシーラベルに悪影響を及ぼすことがあります。
- 提供していない機能のためにデバイス識別子を収集する。
--enable-experimentフラグが必要である。実験機能が必要な、放置されたリポジトリはビルドできなくなります。
また、次の項目も考慮し、通常は受け入れません。インターフェースで隔離されていない、単一の保守担当者に依存するパッケージ、機能が数百行程度の自社製 Dart コードで実現できるもの、入力の手間を省くだけのものです。ライセンスも記録してください。寛容なライセンス(MIT、BSD、Apache-2.0)はベンダー化という代替手段の前提条件です。コピーレフトの依存関係では、その選択肢が失われます。チェックリストの全容は references/dependency-gate-and-audit.md を参照してください。
推移的な依存関係ツリーの監査
dart pub add --dry-run <package> # see what would resolve, resolve nothing
dart pub deps --json > /tmp/deps.json
scripts/audit-deps.sh # walks the resolved set, flags banned patterns
この監査では、解決済みの依存関係グラフ全体をたどり、各名前をポリシーパターンのリストと照合して、該当項目を direct または TRANSITIVE として記録し、APK に含まれる依存関係と、ビルド/テスト専用の依存関係を分けます(禁止パッケージが dev_dependencies からのみ到達可能な場合、バイナリには含まれないため、出荷上の問題ではありません)。終了コード 1 は、その依存関係を拒否するか、該当パッケージを取り込まないものを探す必要があることを示します。パッケージを導入した依存関係を調べるには、dart pub deps | grep -B4 <name> を実行してください。
BANNED/ALLOW リストは、自分のポリシーに合わせて scripts/audit_deps.py の先頭で編集してください。ALLOW の各項目には、その横に必ず文書化された正当な理由を記載してください。
アップグレード
自分のスケジュールに合わせて dart pub outdated を実行してください。ボットはありません。**Dependabot は pub に対応したことがなく、Renovate は、個人運営のリポジトリには対応するワークフローがないにもかかわらず、PR を作成します。**役立つアイデアだけを手作業で取り入れてください。アプリの中核機能が依存するネイティブプラグインを、内容を確認せずにアップグレードしてはいけません。
**CI が成功しても、ネイティブ機能が動作する証拠にはなりません。**ヘッドレスエミュレーター上の CI ジョブでは、アプリが音声を再生する、センサーを読み取る、プラットフォームチャネルとエンドツーエンドで通信するといった動作を証明できません。integration_test で確認できるのは、チャネル呼び出しが「発行された」ことまでです。それ以上は確認できません。機能上重要なネイティブプラグイン、または SDK を更新した後は、実機で手動テストを行ってください。コード生成パッケージ(drift、freezed、json_serializable)に関わるアップグレードの後は、コードを再生成してコミットしてください。古い生成コードは CI で検出されますが、アナライザーでは検出されません(run-codegen を参照)。
単一の保守担当者に依存するプラグインのベンダー化
単一の保守担当者が管理するネイティブプラグインのうち、動作しなくなるとアプリをビルドできなくなるものは、事前に対策を計画する価値のある唯一の依存関係です。対策として、初日から薄いインターフェースを作成し、ベンダー化の際に lib/ に触れずに済むようにします。
// A port the app depends on; the plugin lives behind it. The `Gateway` suffix
// marks a thin wrapper over a SPECIFIC plugin (here flutter_secure_storage) —
// `naming-conventions` owns that Service-vs-Gateway distinction.
abstract interface class SecureStorageGateway {
Future<void> write(String key, String value);
Future<String?> read(String key);
}
プラグインに実際の問題が発生した場合に限り(Flutter のリリースに対してビルドできなくなった、または上流で修正されないリグレッションがリリースされた場合)、最後に正常だったタグで third_party/<plugin>/ にクローンし、pubspec の参照を path: に変更し、SHA と変更したすべての行を VENDORED.md に記録し、ライセンスで再配布が許可されていることを確認して、リファクタリングではなくパッチを適用してください。手順と適用条件の全容は references/vendoring-behind-an-interface.md を参照してください。examples/vendored_plugin_behind_interface.dart も参照してください。
依存関係の削除
pubspec のエントリーを削除し、flutter pub get を実行して、同じコミット内で pubspec.lock の差分をコミットしてください。その後、grep -r "package:<name>" lib/ を実行します。推移的に解決可能なインポートは現在はコンパイルできても、重要なクローンで問題を引き起こします。
マルチパッケージ(ワークスペース)の場合
Dart の pub ワークスペース/モノレポでは、各パッケージが範囲を指定した独自の pubspec.yaml を保持しますが、ワークスペース全体でルートにある共通のロックファイルを解決します。そのルートのロックファイルを監査し、コミットしてください。監査は、単一パッケージではなく、ワークスペース全体の解決済み依存ツリーに対して実行します。単一パッケージの pubspec からは、兄弟パッケージが共通の解決結果に持ち込む依存関係を確認できません。ワークスペースのツールチェーンの仕組みについては codegen-and-toolchain を参照してください。
アンチパターン
pubspec.yamlでバージョンを完全固定する。 次の SDK アップグレードが、解決不能な競合のデバッグ作業になってしまいます。バージョンはロックファイルですでに固定されています。- アプリで
pubspec.lockを Git の追跡対象から外す。 新たにクローンすると、テスト時の依存関係ではなく、その時点でpubが選ぶ依存関係が解決されます。 - ロックファイルの差分をコミットせずに pubspec を編集する。 次の環境では異なる依存グラフが解決されます。
- 依存関係が安全かどうかを判断するために
pubspec.yamlを読む。 禁止対象の SDK が二段目の依存関係にあれば、そこには現れません。 - ネイティブ機能について、CI が成功したから問題ないと信頼する。 エミュレーターには音声エンジンや実際のセンサーがありません。CI の成功が証明するのは Dart のコンパイルであり、機能が動作することではありません。
- 問題のないプラグインを先回りしてベンダー化する。 問題が起きる前から保守の負担を背負うことになります。安価な保険になるのはインターフェースであり、フォークではありません。
- リファクタリングを伴うベンダー化をする。
third_party/で変更したすべての行は、永続的に自分たちが保守する行になります。問題を修正したら、そこで止めてください。 - インターフェースのない、バス係数が 1 のパッケージを追加する。 劣化したときに、その背後へベンダー版を導入するための境界がありません。
完了の定義
- 新規または変更した依存関係にはキャレット範囲を使用し、どの
pubspec.yamlにも完全固定を使っていません。 -
pubspec.yamlの変更と同じコミットにpubspec.lockの差分をステージングしています。 -
scripts/audit-deps.shが終了コード 0 を返します(または、検出された項目すべてに正当なALLOWエントリーがあります)。 - SDK を更新する場合:バージョン記録を更新し、バージョン固定された lint の
include:ファイルが解決済みパッケージ内に存在することを確認しています(references/sdk-pin-and-lint-include.md)。 - 新しい依存関係のライセンスを記録しています。ベンダー化の候補である場合は、許容的なライセンスであることを確認しています。
- 機能上重要なネイティブプラグインはインターフェースの背後に置き、そのプラグインの更新後に実機での確認を行っています。
- 削除後に
grep -r "package:<name>" lib/の検索結果が空です。
関連スキル
codegen-and-toolchain— ワークスペースのリンク、ツールチェーンレベルでの SDK 固定、生成コードをコミットするか Git の追跡対象から外すか。run-codegen— コード生成パッケージのアップグレード後に実行する、決定論的なbuild_runnerの処理。lint-and-style-config— バージョン固定されたinclude:が読み込む、厳格なanalysis_options.yaml。service-boundary-and-native— ベンダー化の境界が依存する、副作用ごとに注入可能なインターフェースを設けるパターン。naming-conventions—ServiceとGatewayの境界サフィックス規則を定めています。SecureStorageGatewayのようなプラグインラッパーにはGatewayサフィックスを使用します。ci-pipeline-and-gates— 監査とロックファイルの最新性チェックを CI に組み込みます。
参考資料
- Dart — パッケージの依存関係とバージョン制約:https://dart.dev/tools/pub/dependencies
- Dart —
pubspec.lockと用語集:https://dart.dev/tools/pub/glossary#lockfile - Dart —
dart pub deps:https://dart.dev/tools/pub/cmd/pub-deps - Dart —
dart pub outdated:https://dart.dev/tools/pub/cmd/pub-outdated - Dart — Pub ワークスペース:https://dart.dev/tools/pub/workspaces
- Flutter — プラグインパッケージ/パス依存関係の追加:https://docs.flutter.dev/packages-and-plugins/using-packages