overfetch: リクエスト監査
あなたは誰かが実際に行動に移すレポートを作成しています。それは、このアプリが必要以上に多くのリクエストを行っている方法をランク付けしたリストであり、それぞれに証拠、修正方法、そして削減すべきリクエスト数が含まれます。このレポートは、すべての発見事項が懐疑的な読者にも耐えうる場合にのみ有用です。したがって、以下の方法は検証に大部分の労力を費やし、何がチェックされ、何がチェックされなかったかを明確に述べます。
あなたが提供するもの
1つのMarkdownレポート(テンプレートは references/report-template.md)に以下を含めます:
- スコープと証拠:どのツリー、どのコミット、どのキャプチャか。
- カバレッジ:すべてのルート、レイジーチャンク、ゲート付きデータサイト、およびそれらのうちコード上でのみ読み取られたものとキャプチャで実行されたもの。
- リクエスト数の増加率でランク付けされた発見事項。それぞれに
file:lineまたはキャプチャ証拠、根本原因、修正パターン、期待される削減効果、工数。 - 非発見事項:チェックしたが除外したパターンとその理由。これにより、次の人が再調査するのを防ぎます。
- 未解決のプロダクト判断とバックエンドへの依頼:クライアントのパッチではなく、人間またはAPIの変更が必要な事項。
- 制限事項:監査で測定できなかったこと。
その後、修正を実装することを提案します。発見事項ごとに1つのPRで、優先度の高いものから順に行います。監査中に修正を開始しないでください。
原則
正規表現のヒットは候補であり、発見事項ではない。 scripts/static_scan.py はテキストを読み取ります。3行のタブストリップと3,000行のテーブル、または意図的なルート無効化(一括インポート後)と怠惰な無効化を区別できません。報告しようとするすべての候補の背後にあるコードを読みましょう。何を読んだかを述べてください。
測定されたものが主張よりも優先される。 実行中のアプリのキャプチャはリクエスト数に関する真実であり、コードはその理由に関する真実です。コメント、ドキュメント、古い監査記録は主張です。ドキュメントに「60秒間フレッシュ」と書かれていて、コードに staleTime: Infinity と書かれている場合、コードを報告し、矛盾を指摘し、読んだコミットハッシュを記録してください。
ライブラリの実際のセマンティクスを知ってから非難すること。 最もコストがかかるミスは、間違っていると確信している主張です:「キャッシュはこれを重複排除する」とキーが異なる場合、「ポーリングはアイドル時に一時停止する」と非表示時にのみ一時停止する場合、「3つの呼び出し元、1分間に3回のフェッチ」とライブラリが同じキーのタイマーを再同期してフェッチを共有する場合。references/library-facts.md には、TanStack Query、SWR、Apollo、RTK Queryの事実が、それぞれ検証したソースパスとともに記載されています。使用中のライブラリのセクションを読んでから、キャッシュやポーリングに関する文を書き、主張が発見事項を決定する場合、インストールされているライブラリのソースを開いて確認してください。
数ではなく増加率でランク付けする。 行数、マウントされたコンポーネント数、または時間とともにスケーリングする読み取りは、固定された重複よりも悪いです。以下の重大度ルーブリックは、増加クラスに関するものです。
カバレッジには分母がある。 キャプチャは、実行中にロードされたコードのみを確認します。誰かが訪れたルート、誰かが開いたダイアログやタブ、オンだったフラグ、バックエンドがたまたま返した状態です。レイジーチャンク、ゲート付き画面、エラーパスは、まさにリクエストのバグが潜む場所です。なぜなら、開発中に誰もそれらを見ないからです。したがって、すべての監査は完全なサーフェス(coverage_map.py)から始まり、すべての「問題なし」はカバーされた範囲に限定され、レポートはどの部分がライブで実行され、どの部分が読み取りのみだったかを示します。
問題ないことも述べること。 行ごとに1つの重複排除された読み取りであるホバープリフェッチは、非発見事項です。それを1つとして書き留めてください。問題のみをリストしたレポートは、読者に他に何が見逃されたのか疑問に思わせます。
数値はどこかから来る。 レポート内のすべてのカウントは、キャプチャファイルまたはコードパスにトレース可能です。直感から「6以上」や「およそ100」を発明しないでください。測定できない場合は、「未測定」と述べ、式(行数 × 行あたりの読み取り回数)を提供してください。
ワークフロー
事前にユーザーに所要時間を伝えてください。中規模アプリの静的のみの監査は、読むのに15〜30分かかります。ライブキャプチャを追加すると、重要なジャーニーの数に応じて15〜45分追加されます。
1. スコープ
- アプリのルートと
package.jsonからデータライブラリを見つけます(スキャンは両方を出力します)。モノレポの場合:各アプリを個別にスキャンします。 - データ層を見つけます:クエリキーファクトリ、トランスポート/サービスのモジュール、
QueryClientの構築(またはSWR設定、Apolloクライアント、RTKcreateApi)。最初にデフォルトを読みます。それらはすべての発見事項が測定されるベースラインを設定します。 - 重要な画面をリストアップします:通常はサインイン後のランディングルート、最も忙しいリスト、および長時間開いたままの画面(シェル、モニター)。明白でない場合はユーザーに尋ねます。それ以外の場合は、これら3つを選択してそう述べます。
- コミットを記録します:
git rev-parse HEAD。レポートのすべての行はこのリビジョンを参照します。 - ライブキャプチャが可能かどうかを判断します:実行中の開発サーバーと操作可能なブラウザ、またはユーザーがすでに持っているHAR。どちらもない場合、監査は静的のみとなり、レポートはその旨を最初の段落で述べる必要があります。
2. カバレッジマップ
python3 <skill-dir>/scripts/coverage_map.py <app-root> --format md --out coverage.md
これが分母です。すべてのルート(ファイルベースのルーターの場合:TanStack Router、Next.js、Remix、SvelteKit、SolidStart。コード定義ルーターの場合:パスリテラル。その他の場合:エントリファイル)、動的な import() バウンダリとその背後にある読み取り、フラグ、プラットフォーム、権限、環境チェックによってゲートされたすべてのデータモジュール、ソース内のすべてのエンドポイントパスリテラル、およびどのルートも到達しないデータモジュールをリストします。2,000ファイルのアプリで数秒で実行されます。
3つの方法で使用します:
- 計画のために。 下部のキャプチャ計画では、最も多くの読み取りサイトがあるルート、トリガーするレイジーチャンク、トグルするゲートが示されています。ステップ6のジャーニーを推測ではなく選択するために使用します。
- スキャンのスコープを設定するために。 どのルートも到達しないモジュール内の発見事項は、デッドコードであるか、マップが解決できない何かによって到達されます。ライブと呼ぶ前にどちらかを確認します。
- ループを閉じるために。 キャプチャ後にキャプチャとともに再実行します。どのルートが訪問されたか、どのモジュールとレイジーチャンクがロードされたか(開発サーバーキャプチャの場合は正確に)、どのゲートモジュールが実行されたか、どのエンドポイントパスが表示されたかがマークされます:
python3 <skill-dir>/scripts/coverage_map.py <app-root> --har home.har --har library.har --visited home.har.visited.txt --format md
最も多くの読み取りサイトがあるルートとレイジーチャンクがカバーされるまでキャプチャを繰り返すか、なぜカバーされなかったかを書き留めます(フィクスチャデータがない、必要なロールがない、実行できないプラットフォーム)。
3. 静的スキャン
python3 <skill-dir>/scripts/static_scan.py <app-root> --format md --out scan.md
python3 <skill-dir>/scripts/static_scan.py <app-root> --format json --out scan.json
スキャンは高速で(2,000ファイルで約10秒)、Python 3.9+のみが必要です。重大度、信頼度、発火した検出器、file:line、重要性、修正パターン、「最初に検証」の指示を含む候補を出力します。scan.md 全体を読んでください。JSONはスクリプトと評価ハーネス用です。
デフォルトで見逃される生成ツリーには --exclude を、リストが長く最初のパスを厳しくしたい場合は --min-confidence medium を追加します。
4. 候補の検証
次の順序で候補を処理します:まずP0を信頼度順に、次にP1、そして残り。保持する各候補について、ファイルを開いて次の質問に答えられる必要があります:
- 行あたりまたはオブザーバーあたりの乗数は何か、そしてそれを制限するものは何か?(固定されたenumに対する
.mapはN+1ではありません。ページネーションされたリストからの行数はそうです。) - キャッシュはすでにそれを吸収しているか?(同じキー、ウォームエントリ、
enabled: falseの読み取り、すでにオブジェクトをフェッチした親。) - クライアントが代わりに使用できるバッチまたはプロジェクションエンドポイントはあるか?APIクライアントまたはOpenAPI仕様を読んでください。存在しない場合、修正はバックエンドへの依頼であり、発見事項は「backend-blocked」とマークされたままになります。
references/catalog.mdからの修正パターンの名前は何か?
これらのチェックに失敗した候補はドロップし、ドロップしたもののうち読者が妥当に提起する可能性があるものは保持します。それらは非発見事項テーブルに1行の理由とともに記載されます。
各検出器のカタログエントリには、特定の非発見条件がリストされています。スキャンに表示されるすべての検出器IDについてそれを読んでください。カタログは同じIDで編成されています。
5. スキャナーができない横断的チェック
これらはgrepではなく読む必要があります。すべてを行ってください。ほとんどの監査で少なくとも1つは見つかります。
- キーの正規性。 最も読み取られている5つのエンドポイントを選び、それぞれを読み取るすべてのクエリキーを見つけてください。1つの読み取りに対して2つのキーは、2つのキャッシュエントリと2つのフェッチです。特に、異なるプロジェクション(完全版と名前のみ)でのリスト読み取りと、実際には異なるデータではないスコープの違い(組織とワークスペース)を探してください。
- 共有された進行中の読み取り。 同じフレームに同じキーでマウントする2つのコンポーネントは、その時点でキーが同一である場合にのみ1つのリクエストを共有します。1ティック後に確定する状態(別のクエリから到着するID)から構築されたキーは、2つのフェッチを生成します:1つは
undefinedで、もう1つは実際の値です。依存キーのenabledゲートをチェックしてください。 - サーフェスごとのフレッシュネス。 データファミリー(ID、リスト、詳細、ライブステータス)ごとに
staleTimeのテーブルを作成し、それぞれが実際にどの程度変化するかと比較してください。通常のバグは、何にも適さない1つのグローバル値です。 - ポーリングの所有権。 共有フックまたはオプションファクトリ内のすべてのポーリングオプションについて、同時にマウントできるコンポーネントと、それぞれが使用するキーをリストします。SWRとApolloでは、各インスタンスが独自のタイマーを実行し、重複排除ウィンドウ内に入らない限り、フェッチが増加します。TanStack Queryでは、タイマーはすべてのクエリ更新で再起動され、1つのフェッチを共有するため、同じキーのオブザーバーは最短間隔あたり1つのフェッチになります。積み重ねはキーまたはクライアント間で発生し、発見事項はケイデンスの所有権です。RTK Queryでは、キャッシュエントリごとに1つのタイマーが最短間隔で実行されます。
- フォーカスと可視性。 このプラットフォームでフォーカスリフェッチをトリガーするものは何か?デスクトップシェル(Tauri、Electron)は多くの場合、OSウィンドウフォーカスを配線し、タブの可視性よりもはるかに頻繁に発火します。非表示時に一時停止するポーリングと、ユーザーが表示画面でアイドル状態の間も実行し続けるポーリングはどれか?
- 同時実行の上限。 ファンアウト読み取りの制限はあるか、それは何か?進行中6件の制限は、1秒間に6リクエストの制限ではないことに注意してください。一方を他方として書かないでください。
- ページネーションの契約。 未知の総数のコレクションの場合、クライアントはページが少なくなって戻ってきたときに停止するか、空のページになるまで尋ね続けるか?ページサイズを画面が表示できるものと比較してください。
- リトライ。 クライアントは403/404/422で何をするか?ライブラリのデフォルトは3回リトライします。禁止されたチャンクを持つ100行のN+1は、400リクエストになります。
- 永続化。 キャッシュが永続化されている場合(IndexedDB、localStorage、ネイティブストア)、
maxAgeは何か、デプロイ時に何がそれを破棄するか、ストアはプラットフォームごとにあるか?永続化されたキャッシュは、キャプチャにおける「コールドロード」の意味を変えます。どちらを測定したかを述べてください。 - ミューテーション。 最も一般的な5つの書き込みについて、何が無効化されるか?小さな書き込み後のルート無効化は通常の発見事項です。
6. ライブキャプチャ
アプリを実行できるか、ユーザーがキャプチャを提供した場合、数値を取得します。references/capture.md には、実行するジャーニー、状態マトリックス(空、データあり、エラー、遅い、オフライン、アイドル、非表示、ゲート付き、コールドおよびウォームキャッシュ)、キャプチャ方法(DevTools HAR、Playwright、バンドルされた scripts/capture.mjs、またはアプリ独自のリクエストログ)、およびキャプチャを比較可能に保つ方法が記載されています。
習慣ではなく、カバレッジマップの計画からジャーニーを選択してください。実行できる場合は開発サーバーを優先してください。そのキャプチャはロードされたすべてのモジュールに名前を付けるため、マップはどのレイジーチャンクとゲートモジュールが実行されたかを正確に言うことができます。その後、キャプチャを使用してマップを再実行し(ステップ2)、ギャップをキャプチャするか、制限事項の下にリストします。
キャプチャは何が起こったかを示しますが、理由は決して示しません。キャプチャのみの監査では、各因果節をそれが立っている場所で hedged にしてください(「2つのクエリキーと一致」、「おそらく再マウント」)、一度免責事項の冒頭で行うのではありません。読者は単一の文を引用し、hedgeされていない「なぜなら」は免責事項なしで伝わります。
他の誰かが撮ったキャプチャしかない場合、キャプチャだけからそれが何を見逃したかを見ることはできません。そう述べ、単一のセッションが通常見逃すもの(訪問されていないルート、開かれていないダイアログやタブ、フラグとロールのバリアント、エラーと空の状態、アイドルおよび非表示タブの動作)をリストし、それを閉じるために必要なキャプチャを推奨してください。
python3 <skill-dir>/scripts/har_analyze.py capture.har --format md --out capture.md
これにより、ページごとのAPIリクエスト、1つのパステンプレートでの行ごとのバースト、キーごとにスタックされたタイマー数によるポーリング、ウィンドウ内の重複(POSTの場合はボディハッシュを含む)、シリアルチェーン、繰り返しエラー、最大のレスポンスが報告されます。静的アセットは除外されます。テレメトリホストには --ignore を使用してください。
読み取り時に区別すべき2つのこと:
- 同じURLへのPOSTが2回ある場合、ボディハッシュが一致する場合のみ重複です。ボディがキャプチャされなかった場合、ツールは「未検証」と言います。そのように報告してください。
- シリアルチェーンは、後のリクエストが前のリクエストからの何かを必要としなかった場合にのみウォーターフォールです。ツールは形状を見つけます。コードで依存関係を確認してください。
7. 調整
キャプチャがその画面をカバーする場合、すべての静的な発見事項をキャプチャ証拠とペアにします。それらが一致しない場合、カウントについてはキャプチャが優先され、原因についてはコードが優先されます。キャプチャがどの候補でも説明できない増幅を示す場合、コードを読んで説明できるまで続けてください。それは通常、ステップ4からのキーの正規性または依存キーの問題です。
リポジトリに以前の監査またはパフォーマンスドキュメントがある場合、あなたの発見事項に触れるその主張のそれぞれをこのコミットのコードと照らし合わせ、矛盾をレポートにリストしてください。古い主張は、同じバグが2回「修正」される方法です。
8. ランク付けと記述
次のルーブリックを使用します:
| 重大度 | 増加クラス | 典型的な形状 |
|---|---|---|
| P0 | 上限なく増加:行数、マウントされたオブザーバー数、または時間とともに | ページネーションされたリストに対する行ごとのクエリ、共有フック内のインスタンスごとのポーリングタイマー(SWR、Apollo、手書き)、キーストロークごとの無効化、クリアされていないインターバル |
| P1 | ホットパス上の固定乗数 | すべての画面が読み取るリストでの staleTime 0、フォーカスごとのリフェッチ、キャッシュなしのエフェクト駆動フェッチ、制限のないリトライ |
| P2 | 固定された絶対的な増加 | レンダリングごとの重複、同じ画面で同じエンドポイントを読み取る2つのキー、バンドルされていないダイアログのマウント効果 |
| P3 | 認知/保守の問題 | スコープされていない無効化、説明のないマジックナンバー、擬似コードのドキュメント、デバッグ用の残骸 |
レポートでは、発見事項をP0(証拠付き)→ P1 → P2 → P3 → 非発見事項の順にリストします。
9. 提供
レポートをユーザーに提供します。その後、フェッチごとに1つのPRで修正を実装するオプションを提供します。優先度の高いものから順に行います。監査をPRに混ぜないでください。
リファレンス
<skill-dir>/references/ には以下が含まれます:
catalog.md:すべての検出器と修正パターンのカタログ。各検出器にはID、名前、重要性、修正パターン、検証手順、非発見条件が含まれます。library-facts.md:TanStack Query、SWR、Apollo Client、RTK Queryの確認済みの挙動(ソースへのリンク付き)。capture.md:キャプチャの計画と実行の手順(ジャーニー、状態マトリックス、ツール、比較可能性)。report-template.md:レポートのMarkdownテンプレート。
スクリプトは <skill-dir>/scripts/ にあり、以下が含まれます:
coverage_map.py(カバレッジマップの生成)static_scan.py(静的スキャン)har_analyze.py(HARファイルの解析)capture.mjs(Playwrightを使用したブラウザキャプチャ)