codeprobe-performance について
コードベースの性能とスケーラビリティを監査するコードレビュー用スキルです。クエリ、メモリ使用、キャッシュ、並行処理、アルゴリズム効率、フロントエンド性能を確認します。
主な機能
- 01N+1と無制限クエリの検出
- 02インデックスとキャッシュ動作の確認
- 03メモリとアルゴリズム効率の問題を指摘
- 04並行処理のリスクを確認
- 05フロントエンド性能の問題を検出
活用シーン
- 01リクエスト処理のクエリ負荷の監査
- 02大規模データ処理のレビュー
- 03キャッシュ無効化とジョブの冪等性の確認
- 04高コストな描画やインポートの特定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ machineroom-dev/ codeprobe --skill codeprobe-performanceインストール後、Claude Code で/codeprobe-performanceを入力して読み込まれたか確認します。
詳細
スタンドアロンモード
オーケストレーター経由ではなく直接呼び出された場合は、最初に次の操作を行ってください。
- 出力規約、実行モード、制約を確認するため、
../codeprobe/shared-preamble.mdを読み込みます(ユーザーのプロジェクトではなく、この SKILL.md の場所を基準に、兄弟ディレクトリであるcodeprobeスキルディレクトリからの相対パスとして解決します)。 - プロジェクトの技術スタックに応じて、該当する参照ファイルを
../codeprobe/references/から読み込みます(同じ方法で解決します)。 - ユーザーから別の指定がない限り、デフォルトで
fullモードを使用します。
パフォーマンスとスケーラビリティの監査
対象範囲
このサブスキルでは、次のカテゴリにわたるパフォーマンスとスケーラビリティの問題を検出します。
- N+1 クエリ — ループ内でのリレーションシップの遅延読み込み
- インデックスの不足 — インデックスのない列を対象とした WHERE/ORDER BY
- 上限のないクエリ — ページネーションや上限を指定しない Model::all()
- メモリ — ファイル全体をメモリに読み込む処理、ループ内での配列への蓄積
- キャッシュ — 同一クエリの繰り返し実行、TTL の不足、書き込み後のキャッシュの陳腐化
- アルゴリズム効率 — ホットパスでの O(n^2)、入れ子のループ、ループ内でのソート
- 並行性 — 競合状態、冪等でないキュージョブ、共有された可変状態
- フロントエンドのパフォーマンス — 不要な再レンダリング、バンドルサイズ、遅延読み込みの不足
検出対象外
- ホットパス以外のコードに対する時期尚早な最適化 — 適切な設計は処理の規模に応じて異なります。起動時に一度だけ呼び出されるユーティリティ関数に、リクエストハンドラーと同じ最適化は必要ありません。
- 開発時のみのデバッグクエリ — シーダー、開発時のみのコマンド、デバッグエンドポイント内のクエリ。
- バッチ処理スクリプト — マイグレーションやデータの一括更新など、すべてのデータを処理することを意図したスクリプトでは、上限のないクエリが適切な場合があります。
- 小規模で上限のあるコレクション(100 件未満)に対する O(n^2) — 要素数が少なく既知の配列に対する入れ子のループは問題ありません。
- フロントエンドの SSR/ビルド時コード — サーバーコンポーネントやビルドスクリプトのパフォーマンス特性は、クライアント側のコードとは異なります。
検出手順
N+1 クエリ
| ID プレフィックス | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | イーガーロードなしでループ内から Eloquent のリレーションシップにアクセスする処理 | コレクションを反復処理する foreach/for ループを検索し、ループ本体内でリレーションシップのプロパティ(例:$order->items、$user->profile)にアクセスしているか確認します。コレクションを取得したクエリで、そのリレーションシップに対して with() または load() が指定されているか確認します。 | Major |
PERF | 反復処理内での ORM の遅延読み込み全般 | ループ内で暗黙的にデータベースクエリが実行されるパターンを探します。たとえば、反復ごとにアクセスされる Django の QuerySet、SQLAlchemy の遅延読み込み、.map() 内での Prisma のリレーションシップへのアクセスなどです。 | Major |
PERF | クエリを発生させるテンプレート/ビュー | Blade テンプレート、Jinja2 テンプレート、React コンポーネント内で、レンダリング中にクエリを発生させるリレーションシップのプロパティを呼び出していないか確認します。 | Major |
インデックスの不足
| ID プレフィックス | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | インデックスのない列を対象とした WHERE/ORDER BY | クエリ条件(where()、orderBy()、WHERE、ORDER BY)をマイグレーションファイルまたはスキーマ定義と照合し、対応するインデックスがあるか確認します。 | Major |
PERF | インデックスのない外部キー | マイグレーションファイル内で、対応するインデックス定義がない foreignId()、foreign()、references() を確認します。多くの ORM ではこれらが自動的に追加されますが、直接記述したマイグレーションでは不足している場合があります。 | Minor |
PERF | 複合インデックスが必要な複合クエリ | 同じクエリ内で異なる列に対して複数の where() 条件を使用する場合や、複合インデックスが有効な where() と orderBy() の組み合わせを確認します。 | Minor |
上限のないクエリ
| ID Prefix | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | 上限のない Model::all() または SELECT * | .all()、::all()、findAll()、LIMIT のない SELECT * FROM、paginate()、take()、または limit() を検索します。 | Major |
PERF | 大規模なデータセットの処理でカーソル/チャンクを使用していない | 大規模なデータセットの処理時に、get() でコレクション全体を取得する、または読み込む操作です。cursor()、chunk()、lazy()、または同等のストリーミング方式を使用する必要があります。 | Major |
メモリ
| ID Prefix | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | ファイル全体をメモリに読み込む | ユーザーのアップロードファイルや大きなファイルに対する file_get_contents()、可変サイズのファイルに対する fs.readFileSync()、サイズ制限のない Python の open().read()。 | Major |
PERF | ループ内での配列への蓄積 | 上限やクリア処理なしにループ内で増加する配列。ストリーミングまたは yield で処理できる結果をメモリに収集しています。 | Minor |
PERF | ジェネレーターではなく大きなコレクションを使用 | 大規模なデータセットでメモリ効率を高めるには、ジェネレーター(yield)、遅延コレクション、またはイテレーターが適しているにもかかわらず、配列/リスト全体を返しています。 | Minor |
キャッシュ
| ID Prefix | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | 同一リクエスト内で同じクエリを繰り返し実行 | 結果をキャッシュせず、同じクエリパターンを単一のリクエスト/関数呼び出し内で複数回実行しています。 | Minor |
PERF | TTL のないキャッシュ | 有効期限を指定しない Cache::put()、cache.set()、Redis の SET。データを無期限にキャッシュすると、古くなるおそれがあります。 | Minor |
PERF | 書き込み後にキャッシュを無効化していない | 関連するキャッシュエントリーをクリアまたは更新しない書き込み操作(作成/更新/削除)。変更後も古いキャッシュが提供されます。 | Major |
アルゴリズムの効率
| ID Prefix | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | ホットパスにおける O(n^2) 以上の計算量 | 同じコレクションまたは関連するコレクションを反復処理するネストされたループ。ループ内の array_search/in_array/includes()(ループ内の線形検索は O(n^2))。 | Major |
PERF | ループ内でのソート | ループ本体内で呼び出される sort()、usort()、array_sort()、.sort()。 | Major |
PERF | ハッシュマップで置き換え可能な線形検索 | 同じ配列に対して繰り返し使用される in_array()、.includes()、.indexOf()、list.index()。先に Set/dict/ハッシュマップを構築すれば、検索ごとの計算量を O(1) にできます。 | Minor |
並行処理
| ID Prefix | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | 読み取り・変更・書き込みにおける競合状態 | ロックせずに値を読み取り、変更し、書き戻すパターン。並行コンテキストでのカウンターのインクリメント、残高の更新、フラグの切り替えなどです。 | Critical |
PERF | 冪等性のないキュージョブ | 重複実行を確認しないキュー/ジョブハンドラー。すでに作成済みかどうかを確認せずにリソースを作成するジョブ。ジョブが作成するデータに一意制約がありません。 | Major |
PERF | 非同期コンテキストでの共有可変状態 | 非同期ハンドラーからアクセスされるグローバル/モジュールレベルの可変変数、モジュールスコープに保存されるリクエストスコープのデータ。 | Major |
フロントエンドのパフォーマンス
| ID Prefix | 検出対象 | 検出方法 | 重大度 |
|---|---|---|---|
PERF | 不要な再レンダリング | コストの高い計算や、レンダリングごとに新しいオブジェクト/配列の参照を受け取るコンポーネントに対して、React.memo、useMemo、useCallback がありません。JSX の props 内でオブジェクト/配列をインラインで作成しています。 | 軽微 |
PERF | バンドルサイズの大きなインポート | import _ from 'lodash'(ライブラリ全体をインポート)、import moment from 'moment'(date-fns や dayjs で代用できる大規模なライブラリ)、import * as icons from 'icon-library'。 | 軽微 |
PERF | 遅延読み込みの欠如 | ルートレベルのコード分割に React.lazy()/動的な import() を使用していません。初回ページ読み込み時に、負荷の大きいコンポーネントをすべて即時読み込みしています。 | 軽微 |
任意のスクリプト連携
/codeprobe audit の実行中にオーケストレーターが実行する scripts/complexity_scorer.py の出力が利用できる場合は、パフォーマンスのホットスポット候補となる複雑度の高い関数を特定するために使用してください。「high」または「very_high」と評価され、ホットパス(リクエストハンドラー、ループ本体、頻繁に呼び出されるユーティリティ)にも現れる関数は、アルゴリズム効率に関する指摘の有力な手掛かりです。
ID プレフィックスと修正プロンプトの例
すべての指摘には PERF- プレフィックスを付け、PERF-001、PERF-002 のように連番を振ります。
修正プロンプトの例
- 「
OrderController@index(22 行目)で、Order::query()の呼び出しに->with('items', 'customer')を追加し、N+1 問題を修正してください。現在、orders/index.blade.php:15の Blade ループ内で 2 つのリレーションを遅延読み込みしています。」 - 「
CatalogService.phpの 30 行目にあるProduct::all()をProduct::query()->paginate(25)に置き換えてください。全レコードを処理する場合はProduct::cursor()を使用してください。現在のクエリは、すべての商品をメモリに読み込んでいます。」 - 「
ReportGenerator@aggregate(45~60 行目)では、$customers内で$ordersを反復する入れ子のループにより、計算量が O(n*m) になっています。外側のループの前に検索用ハッシュマップを作成してください:$ordersByCustomerId = collect($orders)->groupBy('customer_id')。」 - 「
src/utils/helpers.tsの 3 行目にあるimport _ from 'lodash'を、バンドルサイズを削減するため、import debounce from 'lodash/debounce'のような個別のインポートに置き換えてください。」
コメント
Markdown 対応