mcpField
A
audit-performance

audit-performance

github.com/kensaurus開発941 回インストール2 週間前に更新
リポジトリ

audit-performance について

開発者やコーディングエージェント向けのパフォーマンス監査スキルです。実測でボトルネックを特定してから修正し、再測定で効果を確認します。

主な機能

  1. 01実環境とラボの性能測定
  2. 02Core Web Vitalsとp95の確認
  3. 03フロントエンドとDBの問題特定
  4. 04対象を絞った修正と再測定
  5. 05Sentry、Lighthouse、EXPLAINの利用

活用シーン

  1. 01LCP、INP、CLSの問題調査
  2. 02遅いエンドポイントやN+1の追跡
  3. 03バンドル、画像、読み込みの点検

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/kensaurus/skills --skill audit-performance

インストール後、Claude Code で/audit-performanceを入力して読み込まれたか確認します。

同梱ファイルreferences/loading-priority-2026.md

詳細

パフォーマンス監査スキル

自由度:MIXED — 最適化する項目は自由に選択 [HIGH freedom]; Sentry/Web Vitals と EXPLAIN による測定を優先 [LOW freedom — run exactly]。

監査・修正の例外。 まず測定し、それから最適化します。JS のペイロード → audit-bundle-size。瞬時のナビゲーション → enhance-web-instant-nav。限界点 → test-load。タイムアウト/再試行 → audit-resilience。 修正する場合:測定されたボトルネックだけを変更し、再測定します。気づいたほかの遅い処理経路は修正せず、後続対応として記録してください。既存ファイルを書き換えるのではなく、その場で編集してください。パフォーマンステストを追加するのは、リポジトリで既に管理している場合、またはタスクで求められている場合に限ります。リポジトリに含めない一時的な測定を行ってください。

推論の進め方

  1. 観察 — LCP/INP/CLS、p95、または EXPLAIN/N+1 ループを引用します(file:line)。
  2. 解釈 — ユーザーを待たせているのはバイト列、クエリ、それともメインスレッドの処理ですか?
  3. 分類 — vital-miss/N+1/インデックス不足/バンドル/画像/再レンダリング
  4. 深刻度 — ライブの処理経路で LCP/INP が不良、または p95 が目標値を大幅に超えている場合は Critical です。

例

観察: Sentry の /users の p95 は 2.3s。ハンドラーは findAll の後、ユーザーごとに Order.findByUserId を呼び出しています(app/api/users/route.ts:31)。 解釈: N+1。該当ページの TTFB/LCP に悪影響があります。 分類: N+1。 深刻度: Critical(p95 が 200ms を大幅に超過)。 検出事項: /users | N+1 | 注文を先読みする | その後、再測定

ステップ 0:パフォーマンスデータを収集する [LOW freedom — run exactly]

最適化の前に測定します。複数のソースからデータを収集してください。

本番データ(Sentry)

Sentry のパフォーマンスモニタリングが有効な場合は、本番環境の実測メトリクスを取得します。

sentry:search_events
{
 "organizationSlug": "<ORG_SLUG>",
 "projectSlug": "<PROJECT_SLUG>",
 "regionUrl": "<REGION_URL>",
 "query": "slowest transactions by p95 duration in last 7 days",
 "limit": 20
}
sentry:search_events
{
 "organizationSlug": "<ORG_SLUG>",
 "projectSlug": "<PROJECT_SLUG>",
 "regionUrl": "<REGION_URL>",
 "query": "web vitals LCP INP CLS performance scores in last 7 days",
 "limit": 20
}

パフォーマンス関連の問題を確認します。

sentry:search_issues
{
 "organizationSlug": "<ORG_SLUG>",
 "projectSlugOrId": "<PROJECT_SLUG>",
 "regionUrl": "<REGION_URL>",
 "query": "performance issues slow queries N+1 in last 30 days",
 "limit": 20
}
現在のベンチマークを調査する

最新のパフォーマンス目標を取得します。

firecrawl:firecrawl_search
{
 "query": "web vitals thresholds good score <current year>",
 "limit": 3,
 "sources": [{ "type": "web" }]
}
フィールドデータ(信頼できる情報源) [LOW freedom — run exactly]

ラボデータ(Lighthouse)は診断用です。合否を決めるのはフィールドデータです。

  1. トラフィック上位のオリジンと URL 5 件について、CrUX/PageSpeed Insights のフィールドデータタブを確認し、p75 の LCP/INP/CLS を記録します(まずモバイルを確認)。
  2. アプリに web-vitals/attribution を導入します(ベースビルドではありません)。これにより、LCP 要素、INP を発生させる操作の対象、CLS を引き起こすノードが特定できます。
  3. budget.json を使って Lighthouse CI を実行します(references/loading-priority-2026.md §Budgets を参照)。絶対スコアではなく、リグレッションがあれば PR を失敗させます。

コードに手を加える前に、レポートの「Production Metrics」ブロックに 3 つすべてを記録します。

検証プロトコル [LOW freedom — run exactly]
# Field (source of truth): CrUX / PSI field tab → p75 LCP, INP, CLS
# Lab, repeatable, budgeted:
npx @lhci/cli autorun --config=lighthouserc.json
# Early Hints present?
curl -sv https://example.com/ 2>&1 | grep -E "< HTTP/.* 103"
# Speculation: DevTools → Application → Speculative loads
# bfcache:     DevTools → Application → Back/forward cache → Test
# LCP element: web-vitals/attribution → onLCP(({attribution}) => attribution.element)

パフォーマンス目標

Core Web Vitals
メトリクス良好要改善不良
LCP(最大コンテンツの描画)<2.5s2.5-4s>4s
INP(次の描画までのインタラクション)<200ms200-500ms>500ms
CLS(累積レイアウトシフト)<0.10.1-0.25>0.25
その他の主要メトリクス
  • TTFB:フィールドデータの p75 で <800ms(Google のしきい値)。エッジでは <200ms を目指します。
  • FCP:<1.8s
  • 長時間タスク:クリティカルなインタラクション経路(INP の要因)で 50ms を超えるものがないこと
  • LCP リソースの読み込み遅延:<100ms(TTFB から LCP 画像のリクエスト開始までの間隔。通常は「画像を検出できない/プリロードされていない」状態です)
  • bfcache:すべてのコンテンツページで利用可能にします(DevTools → Application → Back/forward cache)

フロントエンドのパフォーマンス監査 [HIGH freedom]

バンドルサイズの分析
npm run build -- --analyze # framework-specific
npx source-map-explorer 'dist/**/*.js'

チェックリスト:

  • バンドル全体が gzip 圧縮後に <200KB
  • gzip 圧縮後に 100KB を超えるチャンクがない
  • ツリーシェイキングが機能している(バンドルに未使用のエクスポートがない)
  • 重いライブラリを遅延読み込みする(チャート、エディター、マップ)
  • moment.js を使用していない(date-fns または dayjs を使用)
  • lodash を一括インポートしていない(lodash-es または個別インポートを使用)
コード分割
  • ルート単位で分割する(各ページが独自のチャンクを読み込む)
  • 重いコンポーネントをコンポーネント単位で分割する(lazy()/dynamic())
  • ファーストビューより下のコンテンツを遅延させる
画像の最適化
  • WebP/AVIF 形式を使用する(写真に PNG/JPEG を使用しない)
  • 画像サイズを適切にする(モバイルに 4K 画像を配信しない)
  • ファーストビューより下の画像に loading="lazy" を指定する
  • 画面サイズに応じてレスポンシブな srcSet を指定する
  • 画像 CDN を使用する(Cloudinary、imgix、Vercel Image Optimization)
読み込み優先度と投機的読み込み [適用する項目の選択は柔軟に、LCP のルールは厳守]

構文の詳細:references/loading-priority-2026.md。即時ナビゲーションの実装 → enhance-web-instant-nav。

LCP 要素(まず web-vitals/attribution または DevTools → Performance → LCP で特定)

  • LCP 画像に loading="lazy" を指定しない — LCP 画像への lazy 指定は自動的に Critical の問題となります
  • LCP 画像に fetchpriority="high" を指定する(Next.js:<Image priority />。これにより preload も挿入されます)
  • LCP 画像を HTML から検出できるようにする — CSS の background-image にしない、クライアント JS から挿入しない、ハイドレーション境界の背後に置かない
  • LCP 画像がレスポンシブで、フレームワークによってすでに preload されていない場合は、<link rel="preload" as="image" imagesrcset imagesizes> を指定する
  • 重要度の低い画像:loading="lazy" decoding="async" を指定する。<picture> では AVIF → WebP → JPEG のフォールバックを用意する

フォント

  • font-display: swap を指定する(ブランド以外のテキストでは optional も可)。swap による CLS が発生しないよう、フォールバックのメトリクス(size-adjust、ascent-override)を設定する — next/font が自動的に設定しますが、実際に使用されていることを確認してください
  • ファーストビューに必要なウェイトのみを preload する。使用する Unicode 範囲にサブセット化し、セルフホストする

Early Hints(103)

  • オリジン/CDN が、画像/フォント CDN への preconnect と LCP 画像+クリティカル CSS の preload を含む 103 Early Hints を送信する。確認方法:curl -sv https://site/ 2>&1 | grep -i "< HTTP/.* 103"
  • Node:res.writeEarlyHints({ link: [...] }) を使用する。それ以外の場合は CDN で設定する

投機的ルール(次のページへのナビゲーションを高速化)

  • サイト内リンクに対する moderate の eagerness の prefetch ドキュメントルールを含む <script type="speculationrules"> を配置する
  • prerender は、次に表示される可能性が高いページ(ページネーション、「次の記事」、商品詳細など)のみに使用し、eagerness は moderate/conservative にする
  • 投機的読み込みの対象外:ログアウト、カート/チェックアウト、認証、GET に副作用のあるもの、expects_no_vary_search が設定されていない ? 付き URL
  • Sec-Purpose: prefetch;prerender が存在する場合、サーバーは副作用を無視する。分析/広告は document.prerendering + prerenderingchange で制御する
  • 完全な実装 → enhance-web-instant-nav

bfcache

  • unload リスナーを使用しない(pagehide を使用する)。HTML に Cache-Control: no-store を指定しない。pagehide 時に WebSocket や IndexedDB トランザクションを開いたままにしない
  • DevTools → Application → Back/forward cache → 「Test back/forward cache」で緑色の表示になることを確認する

レンダリング戦略(Next.js 16/React 19)

  • デフォルトで Server Components を使用する。インタラクティブな末端部分にのみ 'use client' を指定する
  • 遅いデータを <Suspense> でラップしてシェルをストリーミングする。ルート全体をブロックするのではなく、静的なシェル+動的な穴(cacheComponents/'use cache')を使用する
  • 手動の memo/useMemo/useCallback を使う前に React Compiler を有効にする(reactCompiler: true)。手動のメモ化は、Profiler で Compiler が最適化できていないと確認された場合にのみ行う

INP(応答性)

  • タップ/クリック/キー入力の処理経路に 50ms を超えるタスクを置かない。scheduler.yield()(フォールバックは setTimeout(0))または startTransition で処理を分割する
  • 入力ハンドラー内で同期的なレイアウト読み取り(offsetHeight、getBoundingClientRect)を行わない
  • 画面外にある長いセクションに content-visibility: auto + contain-intrinsic-size を指定する
  • サードパーティスクリプト:async/defer を指定する。Next の <Script strategy="lazyOnload">(安全な場合は Partytown 経由で "worker")を使用し、同意後に読み込み、埋め込みにはファサードを使用する(lite-youtube、静的な地図画像からクリック時に iframe を表示)

CLS

  • すべての <img>、<video>、<iframe>、広告枠に width/height または aspect-ratio を指定する
  • 後から表示される UI(バナー、トースト、Cookie バー)はスペースを確保するか、オーバーレイ表示にする — コンテンツを押し下げない
React のパフォーマンス(該当する場合)
  • React Compiler を有効化し、手動のメモ化は Profiler の根拠がある場合にのみ行う(「読み込みの優先順位と先読み」を参照)
  • 不要な再レンダリングがない(React DevTools Profiler)
  • 長いリストを仮想化する(react-window、@tanstack/react-virtual)
  • JSX の props 内でオブジェクトや配列をインライン生成しない
  • Context プロバイダーの適用範囲を狭くする(ローカル状態のためにアプリ全体をラップしない)
CSS のパフォーマンス
  • レイアウトスラッシングがない(読み取りを先に行い、その後に書き込む)
  • アニメーションには transform/opacity を使用する(GPU アクセラレーション)
  • @import の連鎖がない(バンドルする)
  • ファーストビューのコンテンツ用にクリティカル CSS をインライン化する
  • フォントをサブセット化してプリロードする(<link rel="preload">)

バックエンドのパフォーマンス監査 [自由度:高]

データベースクエリ

N+1 の検出:

// BAD: N+1
const users = await User.findAll();
for (const user of users) {
 const orders = await Order.findByUserId(user.id);
}

// GOOD: eager loading
const users = await User.findAll({ include: [Order] });

チェックリスト:

  • N+1 クエリを解消する(イーガーローディング、結合、バッチ読み込み)
  • 頻繁にクエリされる列(WHERE、JOIN、ORDER BY)にインデックスを設定する
  • 必要な列のみを SELECT する(SELECT * を使用しない)
  • すべてのリストエンドポイントにページネーションを設定する
  • コネクションプーリングを設定する
  • スロークエリのログ記録を有効にする

インデックスの検証: [自由度:低 — このとおりに実行]

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = '123';
API レスポンスの最適化
  • レスポンス圧縮を有効にする(gzip/brotli)
  • 適切なキャッシュヘッダー(Cache-Control、ETag)を設定する
  • 過剰なデータ取得をしない(クライアントが必要とするものだけを返す)
  • リストエンドポイントにページネーションを設定する
  • p95 のレスポンスタイムを 200ms 未満にする
キャッシュ戦略
レイヤーツールTTL用途
ブラウザーCache-Control ヘッダー変動静的アセット、API レスポンス
CDNVercel/CloudFront/Cloudflare1h-1d静的ページ、画像
APIRedis/Memcached5m-1hコストの高いクエリ、計算済みデータ
ORMクエリキャッシュ1m-5m同一のクエリの繰り返し
ネットワークの最適化
  • 静的アセットに CDN を使用する
  • HTTP/2 または HTTP/3 を有効にする
  • 重要なオリジンに事前接続する(<link rel="preconnect">)
  • サードパーティのドメインに DNS プリフェッチを設定する
  • 可能な場合は API 呼び出しをバッチ化する(GraphQL、DataLoader)

調査に基づく最適化 [自由度:高]

特定のパフォーマンスのボトルネックについて、最新の解決策を調査します。

firecrawl:firecrawl_search
{
 "query": "<framework> <specific bottleneck> performance optimization <current year>",
 "limit": 5,
 "sources": [{ "type": "web" }]
}

続いて、最適な結果を詳しく読みます。

firecrawl:firecrawl_scrape
{
 "url": "<best-result-url>",
 "formats": ["markdown"],
 "onlyMainContent": true
}

Context7 経由で公式フレームワークのドキュメントを確認します。

context7:resolve-library-id
{
 "libraryName": "<framework>",
 "query": "performance optimization"
}

すぐにできる改善(効果が高く、労力が少ないもの)

問題解決策効果
LCP 画像が遅延読み込み、または優先されていないfetchpriority="high" とプリロードを使用し、LCP 画像から loading="lazy" を削除する高(LCP が 300〜800 ms 短縮)
1 ページ目は速いが、2 ページ目は遅いSpeculation Rules の prefetch(中程度)高(次のナビゲーションの TTFB ≈ 0)
どこでも手動でメモ化しているReact Compiler を有効にする高(INP。冗長なメモ化を削除)
バンドルが大きいルートをコード分割し、重いライブラリを遅延読み込みする高
画像の読み込みが遅いWebP + 遅延読み込み + レスポンシブ対応高
キャッシュがないCache-Control ヘッダーを追加する高
N+1 クエリイーガーロード/バッチ処理高
インデックスが不足しているデータベースのインデックスを追加する高
フォントが最適化されていないサブセット化 + プリロード + font-display:swap中
圧縮がないgzip/brotli を有効にする中
コストの高い再レンダリングまず React Compiler を使用し、手動のメモ化は Profiler の根拠がある場合にのみ行う中
レイアウトシフト画像/埋め込みコンテンツに幅と高さを明示する中

修正を適用する前の自己批評 [自由度:低 — 省略禁止]

  1. 証拠がある — 「遅そうに見える」ではなく、測定済みのバイタル、p95、または EXPLAIN に基づいている
  2. 再現可能 — 同じパスで、対象テーブルのしきい値を引き続き超えている
  3. 深刻度に根拠がある — Critical は、バイタルの不良または本番パスの p95 に該当する
  4. 担当が適切 — JS ペイロード → audit-bundle-size、限界点 → test-load、タイムアウト → audit-resilience
  5. 誤った安心感を与えない — 再レンダリングを測定せずに memo/useMemo を使わない。修正後に再度測定する
  6. LCP の要素を特定する — 画像の修正を推奨する前に、どの要素が LCP なのかを証明する(アトリビューションデータがなければ、監査ではなく推測)

出力:パフォーマンス監査レポート

## Performance Audit: [Project Name]

### Production Metrics (from Sentry)
- LCP: [value] — [good/needs work/poor]
- INP: [value] — [good/needs work/poor]
- CLS: [value] — [good/needs work/poor]
- Slowest transactions: [list with p95 times]

### Critical Issues (fix immediately)
| # | Area | Issue | Impact | Fix |
|---|------|-------|--------|-----|
| 1 | DB | N+1 on /users endpoint | p95 = 2.3s | Eager load orders relation |

### Optimizations (prioritized)
| # | Area | Issue | Impact | Effort | Fix |
|---|------|-------|--------|--------|-----|
| 1 | FE | Bundle 450KB gzipped | High | Low | Code split routes |

### Already Optimized
- [list of performance areas that are well-implemented]

### Monitoring Recommendations
- [what to add to track performance over time]

### Research Sources
- [URL] — [what optimization pattern it provided]

コメント