依存ライブラリを慣用的に使う は何をしますか?
すべての依存ライブラリは、特定の形状の問題に対して構築されています。そのAPIは、そのモデル(何を所有し、何をユーザーに委ね、何を保証し、どこで拡張を期待するか)をエンコードしています。慣用的な解決策、つまりライブラリ自身のドキュメントや標準的な例が示すものを書き、問題の形状が異なる場合(インピーダンスミスマッチ)、その仕事に適したツールを選びましょう。避けるべき失敗は、車輪の再発明、フレームワークとの戦い、そしてそのケースのために作られた機能を静かに活用不足にすることです。
摩擦は匂いです:明白なパスに対するキャスト、パッチ、回避策は、モデルを誤読しているか、ツールが間違っていることを意味します。最もコストがかかる違反は、まったく摩擦がありません。それらは型チェックを通過し、局所的には問題なく見える一方で、ライブラリが存在する目的である保証を無効にします。そのため、意図的に探す必要があります。
依存ライブラリを使ったコードを書く
デフォルトのパスです。レビュー、監査、追加、アップグレードはこれから分岐します(下記)。
-
問題を言い換える。 XY問題に注意してください:タスクは、あなたまたはユーザーから、しばしば解決策として届きます。それを一般的な問題として言い換えます:
- 「アップロードを3回リトライする」は、リトライポリシーです。
- 「各アカウントの最新の請求書に対して
row_number() over (partition by account_id)が必要」は、グループごとのトップNであり、ORMはウィンドウ関数なしでモデル化できる場合があります。
「ライブラリはXができない」という主張は、誰が言ったかに関わらず、ステップ3で検証する必要があります。
完了条件:ライブラリのドキュメントが使う言葉で問題を命名できていること。
-
既に解決されている場所を見つける、近いものから順に:
- あなたがいるフレームワーク:ワークフローエンジンのリトライポリシーとタイマー、GraphQLクライアントのページネーションとストア更新、テストランナーのモックタイマー。
- プロジェクトの宣言済み依存関係(
package.json、pyproject.toml、go.mod、Cargo.toml)とコードベースでのインポート方法。既存の使用法は、ライブラリの設計とは異なるかもしれないプロジェクトの慣習を示します。 - プロジェクトの共有パッケージ。多くの場合、プロジェクトが望む使い方で依存ライブラリをラップしています。
- 使用中のランタイムバージョン(
engines、.nvmrc、requires-python)の標準ライブラリ。広範な標準ライブラリは、人々が覚えているよりも多くのものをカバーしています:Pythonのitertools、collections、functools、pathlib;現代のNodeのfetch、AbortSignal.timeout、util.parseArgs、node:test。
完了条件:すべてのソースを確認し、空の可能性もあるショートリストがあること。
-
インストール済みバージョンのドキュメントからモデルを学ぶ。 ロックファイルからバージョンを取得します。ソースは、それが一致する場合のみ有効です。順番に読んでください:
- この依存関係に関するプロジェクトのガイダンス。GUIDANCE.md に従って見つけられ、維持されています。これは、意図的なプロジェクトの決定に関する権威であり、ライブラリのモデルのキャッシュです。記録されたバージョンがロックファイルと異なる場合、または主張があなたのアプローチを決定する場合は、以下のソースで確認してください。
- リファレンスチェックアウト:プロジェクトにクローンされた、または
opensrcなどのツールで取得された依存関係のリポジトリ。通常、AGENTS.mdまたはCLAUDE.mdにポインタがあります。タグがロックファイルと一致することを確認します。そのドキュメント、例、テストは、意図された使用方法の最も優れた記録です。 - インストール済みパッケージ:エクスポートされた型とそのドキュメントコメント。ただし、多くの場合、ドキュメントや例は削除されています。
- オンライン(最後の2つのソースにドキュメントがない場合):そのメジャーバージョンの公式ドキュメント(コンセプトとガイドページ、およびAPIリファレンス)。次に、一致するタグのアップストリームリポジトリ。
構築するのはパブリックAPI上のみ:ドキュメントに記述され、宣言されたエントリポイント(
exportsマップ、__all__、先頭アンダースコアのない名前)を介して到達できるもの。動作を理解するためにソースを読んでください。ソースだけで見つけたものはすべてプライベートです。SemverはパブリックAPIとそれ以外の何も保護しません。ライブラリに関するあなたの記憶は、ドキュメントに対して検証する仮説であり、たまたま開いたランタイムファイルはその代わりにはなりません。完了条件:ライブラリを使ったコードを書いたり判断したりする前に、ドキュメントから以下の質問に答えられること:
- これは何のために作られ、何を意図的に除外しているか?
- 何を所有し(ストア、キャッシュ、ライフサイクル、リトライ、トランザクション)、何を生成するか、または宣言するように要求するか(コード生成出力、スキーマ、型)、そして各ピースはコードのどの部分のためのものか?
- どの保証を維持しているか(データマスキング、ワークフローの決定性、トランザクション分離)、システムのどの部分間で、そして何がそれを静かに無効にするか?多くの場合、それはあなた自身のモジュール間に存在します。
- これをカバーするパブリックAPIと文書化された拡張ポイント(オプション、プラグイン、ミドルウェア、フック、エスケープハッチ)はどれか、そして標準的な例はどのモジュールが何をインポートするところまでどのように見えるか?
- どのようなチェック(リントプラグイン、コンパイラまたはコード生成の検証、厳格な型モード)を出荷しており、プロジェクトはそれらを実行しているか?
-
適合性を選択する:
- 問題がモデルに一致する: それを使う。
- 一部がメインAPIの範囲外にある: その部分については、文書化されたエスケープハッチを使う(ORMでの生SQL、ヘッドレスコンポーネント、カスタムリゾルバ)。エスケープハッチは設計の一部です。
- インストール済みバージョンにない: チェンジログが新しいバージョンにそれがあることを示している場合、UPGRADING.md に従ってアップグレードを提案します。
- インピーダンスミスマッチ: モデルが適合する依存ライブラリを使うか、このライブラリを避けてプレーンなコードを、私たちが所有するシームの背後で使う。インストールされている、または近くで使われていることは、そのライブラリが適切なツールであることを意味しません。
完了条件:選択し、モデルからそれを正当化できること。
-
ライブラリの流儀で、パブリックAPI上に書く。 最初に動くバージョンは、しばしば慣用的なものではありません。隣接するコードと標準的な例が一致しない場合、標準的な例に従い、ユーザーにその逸脱について伝えます。進めながらライブラリ自身のチェックを実行します。プロジェクトがそれらをオフにしている場合、それらをオンにすることで何が捕捉されるかを述べます。
完了条件:ライブラリのチェックがパスし、以下のスメルが適用されない(または適用される各スメルが報告する選択である)、そしてライブラリのメンテナーがそのコードをライブラリの設計通りに使われていると認識する。
-
摩擦が続く場合、最初に私たちの設計をライブラリのモデルに適応させます。その制約はしばしば教訓をエンコードしています(ワークフローエンジンの決定性ルール、GraphQLクライアントのデータマスキング)。次にステップ4を再検討します。ユーザーの同意がある場合のみライブラリに逆らって作業し、提案と回避策のコメントで依存している文書化されていない動作を明示します。モデルと製品要件の間の競合はユーザーの判断です。
スメル:ライブラリとの戦い
それぞれがあなたをステップ4に戻します。ライブラリが提供するものによってランク付け:敗北した保証は、今日何も壊れていなくても、他のすべてよりも優先され、モデルをエンコードするプロジェクトルールはそのモデルの重大性を引き継ぎます。
- 敗北した保証:生成されたコードが生成されたモジュールの外部で使用されている(別のコンポーネントのフラグメントタイプ)、別のモジュールのマスクされたデータを読み取る、無関係な呼び出し元間で共有された1つのフラグメントまたはスキーマ、決定論的ワークフロー内の非決定性。これらはきれいに型チェックを通過するので、意図的に探します。
- 私たちのコードがライブラリの型と出会う場所でのキャストまたは型チェッカーの抑制(
as、any、@ts-expect-error、# type: ignore)。 - プライベートAPI:内部パス(
pkg/dist/…、pkg._internal)、アンダースコア名、文書化されていないエクスポート、ライブラリオブジェクトのプライベートフィールドへのキャスト、ソースでのみ見つかる動作。不安定または実験的なAPIは文書化されていますが、semverの範囲外です。ドキュメントがここで推奨する場合にそれを使用し、そのように述べます。 - ライブラリの動作を変更するパッチ(パッケージマネージャーのパッチ、モンキーパッチ、ベンダーフォーク)。ドキュメントが既に約束していることを実現させるパッチはバグ修正であり、正しい動きです:修正に留め、アップストリームのイシューにリンクします。
- 重複した状態:ライブラリが所有するもののための2番目の真実のソース。例えば、ストアの状態にミラーリングされたGraphQLキャッシュ、フレームワークが既に追跡しているリトライカウント。
- ライブラリがコールバック、イベント、フックを提供している場合に、ライブラリの「後」にコードを実行するタイマー、フラグ、エフェクトチェーン。
- すべての呼び出しサイトで上書きされるデフォルト値、または呼び出しごとに動作を元に戻すラッパー。
- ラップするAPIのほとんどをリネームするだけの浅いラッパー。削除テストを適用します:それを削除しても呼び出しをライブラリに戻すだけなら、それは存在価値がありません。
- 必要な全体像よりも多く見えたために再構築されたライブラリ機能の一部。
スメル:ライブラリの活用不足
コードはパブリックAPI上で動作しますが、このケースのために作られたアフォーダンスがあれば、より短く、または宣言的にできるでしょう。それを見つけるには、渡す各オプションオブジェクトの型と、ステップ1からの問題に関するライブラリのガイドを読みます。
- 1つの操作を行う複数の呼び出し:upsertがあるのにfind-then-create。バッチAPIがあるのにループでの書き込み。
- 出力を再整形するグルーコード。クエリやフックがselectまたはprojectionオプションを取る場合。
- ライブラリが追跡する状態を手動で再構築:ローディング、ペンディング、ダーティ、リトライフラグ。
- デフォルト、共有設定、テーマ、ファクトリがあるのに、すべての呼び出しサイトで同じオプション。
- ライブラリで宣言できることを命令型コードで行う:スキーマ、バリアント、リレーション、ポリシー。
バランスは両方向に働きます:数行の明確なプレーンコードで仕事が済む場合、プレーンコードが慣用的です。
レビュー、監査、追加、アップグレード
- 他の誰かの変更をレビューする:REVIEWING.md を読んでください。
- コードベースによる依存ライブラリの使用を監査する:AUDITING.md を読んでください。
- パッケージを追加する、または依存関係やランタイムをアップグレードする:最初に UPGRADING.md を読んでください。
- 依存関係に関するプロジェクトのガイダンスを見つける、書く、または更新する:GUIDANCE.md を読んでください。
返答する内容: あなたが書いていない動作を実現するライブラリ機能。あなた自身が書いた汎用コードについては、何をチェックし、なぜ何も適合しなかったか。スキップした各依存関係と、そのモデルのどの部分が一致しなかったか。そして、保持した各スメルとその理由。依存関係について永続的な何かを学んだ場合は、GUIDANCE.md に従って、提案するガイダンスを追加してください。