performance-review について
performance-review は、リリース前、性能低下の調査、高トラフィックのイベント前に Web アプリの性能を測定・評価する手順です。ブラウザで Core Web Vitals、読み込み、リソース、実行時の挙動を測り、レポート、修正案、YAML 回帰テストを作成します。
主な機能
- 01Core Web Vitals の測定
- 02ページ読み込み指標の評価
- 03リソースとバンドルの確認
- 04実行時パフォーマンス分析
- 05性能レポートと YAML 回帰テスト
- 06クイック・標準・詳細レビュー
活用シーン
- 01リリース前に主要ページの性能を確認
- 02読み込みの遅さや操作の遅延を調査
- 03新しい依存関係やサードパーティスクリプトの影響を評価
- 04高トラフィックイベント向けの性能基準を設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ shiplightai/ agent-skills --skill performance-reviewインストール後、Claude Code で/performance-reviewを入力して読み込まれたか確認します。
詳細
パフォーマンスレビュー
Google の Core Web Vitals のしきい値と業界ベンチマークに照らして、アプリケーションのパフォーマンスを測定・評価します。このレビューでは、開発中には見えにくいものの実際のユーザーに影響するパフォーマンス上の問題、つまりバンドルの肥大化、レイアウトシフト、操作の遅延、画像の最適化不足、レンダリングを妨げるリソースを検出します。
使用するタイミング
次の場合に /performance-review を使用してください。
- リリース前、または大規模な機能追加後
- ページの読み込みが遅く感じられるものの、原因がわからない場合
- トラフィックの多いイベントに備える場合
- 新しい依存関係やサードパーティ製スクリプトの追加後
- SEO のランキングがパフォーマンススコアに左右される場合
- ユーザーから遅延や離脱の報告があった場合
参照する標準
- Google Core Web Vitals — LCP、INP、CLS(2024 年のしきい値)
- Google Lighthouse — パフォーマンスのスコアリング方法
- HTTP Archive — ウェブパフォーマンスのベンチマーク(中央値、p75、p90)
- Web.dev Performance Guidelines — ベストプラクティス
- RAIL Model — 応答、アニメーション、アイドル、読み込みの予算
フェーズの概要
Phase 1: EDUCATE → Performance impact on business and what we measure
Phase 2: SCOPE → Identify key pages, performance budget, baseline
Phase 3: ANALYZE → Browser-based performance measurement
Phase 4: REPORT → Findings with metrics, scores, and comparisons
Phase 5: REMEDIATE → Fix guidance + YAML regression tests
フェーズ 1:理解する
重要な理由: ページの読み込みが 1 秒遅れると、コンバージョンが 7%低下します(Akamai)。Google は 2021 年から Core Web Vitals をランキングシグナルとして使用しています。モバイル訪問者の 53%は、読み込みに 3 秒を超えるページから離脱します(Google)。Amazon の調査では、遅延が 100ms 増えるごとに売上が 1%減少します。パフォーマンスは機能の一つであり、その欠如はバグです。
このレビューでは、静的解析だけではなく、ブラウザーで実際のパフォーマンスを測定します。実際の読み込み時間、レンダリングの挙動、操作への応答性を記録します。
フェーズ 2:範囲を定める
コンテキストを収集する
-
コードベースから自動検出する:
- ビルドシステム(Webpack、Vite、Next.js など)
- バンドル分析の設定(ある場合)
- 画像最適化パイプライン(sharp、next/image など)
- フォントの読み込み方式
- コード分割の設定
- Service Worker/キャッシュ戦略
- CDN の設定
-
ユーザーに質問する(一度に 1 つずつ):
- 対象 URL:アプリはどこで実行されていますか?(現実的な測定には本番環境を推奨)
- 重要なページ:パフォーマンス上、最も重要なページはどれですか?(ランディングページ、主要機能のページ、データ量の多いページを推奨)
- パフォーマンス予算:既存の目標はありますか?(デフォルト:Core Web Vitals の「良好」なしきい値)
- 既知の懸念事項:遅く感じるページはありますか?(任意)
-
測定計画を定める:
- テスト対象ページ(重要なページ 3~5 件)
- 条件:デスクトップとモバイルのシミュレーション(Moto G4/低速 4G)
- 指標:Core Web Vitals と補足指標
- ベースライン:初回実行で比較用のベースラインを確立
フェーズ 3:分析する
record_evidence: true を指定して new_session でブラウザーセッションを開きます。対象範囲の各ページについて、すべての測定カテゴリを実行します。
カテゴリ A:Core Web Vitals(CWV)
| チェック ID | 指標 | 良好 | 改善が必要 | 不良 | 方法 |
|---|---|---|---|---|---|
| CWV-01 | LCP(最大コンテンツの描画) | ≤2.5s | 2.5-4.0s | >4.0s | LCP エントリを PerformanceObserver で取得 |
| CWV-02 | INP(次の描画までのインタラクション) | ≤200ms | 200-500ms | >500ms | 主要なインタラクティブ要素をクリックし、遅延を測定 |
| CWV-03 | CLS(累積レイアウトシフト) | ≤0.1 | 0.1-0.25 | >0.25 | layout-shift エントリを PerformanceObserver で取得 |
ブラウザーでの検証: 各ページに移動し、JavaScript で指標を取得します。
// LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lcp = entries[entries.length - 1];
console.log('LCP:', lcp.startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
// CLS
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) clsValue += entry.value;
}
console.log('CLS:', clsValue);
}).observe({ type: 'layout-shift', buffered: true });
カテゴリ B:ページ読み込みパフォーマンス(LOAD)
| チェック ID | チェック項目 | しきい値 | 方法 |
|---|---|---|---|
| LOAD-01 | 最初のバイトまでの時間(TTFB) | ≤800ms | performance.timing.responseStart - navigationStart |
| LOAD-02 | First Contentful Paint(FCP) | ≤1.8s | performance.getEntriesByName('first-contentful-paint') |
| LOAD-03 | DOM コンテンツ読み込み完了 | ≤2.0s | performance.timing.domContentLoadedEventEnd |
| LOAD-04 | ページ全体のサイズ | ≤3MB(モバイル)/≤5MB(デスクトップ) | performance.getEntriesByType('resource') の合計 |
| LOAD-05 | HTTP リクエスト数 | ≤50 | リソースエントリーをカウント |
| LOAD-06 | 操作可能になるまでの時間(TTI) | ≤3.8s | ロングタスクの分析 |
| LOAD-07 | Total Blocking Time(TBT) | ≤200ms | ロングタスク(50ms を超える部分)の合計 |
| LOAD-08 | Speed Index | ≤3.4s | 視覚的な進行状況の分析 |
ブラウザーでの検証: Performance API と performance.getEntries() を使用して、すべての指標を収集します。
カテゴリ C:リソースの最適化(RES)
| チェック ID | チェック項目 | 基準 | 方法 |
|---|---|---|---|
| RES-01 | 画像に最新の形式(WebP/AVIF)を使用している | Web.dev | 画像 URL と Content-Type を確認 |
| RES-02 | 画像が適切なサイズである(大きすぎない) | Web.dev | 表示サイズと実際のサイズを比較 |
| RES-03 | 画像で遅延読み込みを使用している(ファーストビューより下) | Web.dev | ファーストビューより下の画像に loading="lazy" が指定されているか確認 |
| RES-04 | 画像に明示的な寸法(width/height)がある | CLS の防止 | width/height 属性を確認 |
| RES-05 | CSS がレンダリングをブロックしていない(またはクリティカル CSS をインライン化している) | Web.dev | CSS の読み込み方法を確認 |
| RES-06 | JavaScript が遅延または非同期で読み込まれている | Web.dev | スクリプトの読み込み属性を確認 |
| RES-07 | フォントで font-display: swap または optional を使用している | Web.dev | @font-face 宣言を確認 |
| RES-08 | フォントが事前読み込みされている | Web.dev | <link rel="preload" as="font"> があるか確認 |
| RES-09 | Gzip/Brotli 圧縮が有効になっている | HTTP のベストプラクティス | Content-Encoding ヘッダーを確認 |
| RES-10 | HTTP/2 または HTTP/3 が使用されている | HTTP のベストプラクティス | Performance API でプロトコルを確認 |
| RES-11 | キャッシュヘッダーが適切に設定されている | HTTP のベストプラクティス | 静的アセットの Cache-Control を確認 |
| RES-12 | 未使用の CSS/JS が読み込まれていない | バンドルの効率性 | Page.startJSCoverage/startCSSCoverage でカバレッジを確認 |
ブラウザーでの検証: JavaScript を使用して、読み込まれたすべてのリソース、その種類、サイズ、読み込み属性を調べます。詳細なリソース指標を取得するには、performance.getEntriesByType('resource') を使用します。
カテゴリ D:バンドル分析(BUN)
| チェック ID | チェック項目 | しきい値 | 方法 |
|---|---|---|---|
| BUN-01 | メイン JS バンドルのサイズ | ≤250KB(gzip 圧縮後) | メインバンドルの転送サイズを確認 |
| BUN-02 | JS の合計サイズ | ≤500KB(gzip 圧縮後) | すべての JS の転送サイズを合計 |
| BUN-03 | CSS の合計サイズ | ≤100KB(gzip 圧縮後) | すべての CSS の転送サイズを合計 |
| BUN-04 | コード分割が実装されている | ベストプラクティス | 複数の JS チャンクがあるか確認 |
| BUN-05 | 重複する依存関係がない | バンドルの効率性 | チャンクの内容を分析して重複を確認 |
| BUN-06 | ツリーシェイキングが効果的に機能している | バンドルの効率性 | 既知の大規模な未使用エクスポートがないか確認 |
| BUN-07 | 本番環境でソースマップが公開されていない | セキュリティ/パフォーマンス | .map ファイルにアクセスできるか確認 |
| BUN-08 | サードパーティ JS の予算 | JS 全体の ≤30% | サードパーティとファーストパーティの比率を計算 |
ブラウザーでの検証: Performance API を使用して転送サイズを測定します。ソースマップの URL を確認します。スクリプトのドメインのオリジンを分析します。
カテゴリ E:実行時パフォーマンス(RUN)
| チェック ID | チェック項目 | しきい値 | 方法 |
|---|---|---|---|
| RUN-01 | 操作中に長時間タスクが発生しない | 50ms 超 = 長時間タスク | 長時間タスクに対して PerformanceObserver を使用 |
| RUN-02 | スクロールが滑らかに動作する | 60fps | ページをスクロールし、フレーム落ちを測定 |
| RUN-03 | アニメーションのパフォーマンス | 60fps | アニメーションを実行し、カクつきを測定 |
| RUN-04 | メモリ使用量が安定している(リークがない) | 増加傾向がない | performance.memory を時間をおいて測定 |
| RUN-05 | DOM ノードが過剰に存在しない | ≤1500 ノード | document.querySelectorAll('*').length をカウント |
| RUN-06 | レイアウトのスラッシングがない | 強制リフロー 0 件 | 強制的なスタイル再計算を監視 |
| RUN-07 | イベントリスナーが効率的である | リスナーが過剰でない | スロットリングされていない scroll/resize リスナーを確認 |
ブラウザーでの検証: JavaScript でパフォーマンス指標を測定しながら、アプリを操作します。
フェーズ 4:レポート
構造化レポートを shiplight/reports/performance-review-{date}.md に保存します。
# Performance Review Report
**Date:** {date}
**URL:** {url}
**Pages tested:** {list}
**Conditions:** Desktop + Mobile (simulated Moto G4 / Slow 4G)
## Overall Score: {X}/10 | Confidence: {X}%
## Core Web Vitals Summary
| Metric | Desktop | Mobile | Status |
|--------|---------|--------|--------|
| LCP | 1.8s | 3.2s | ⚠️ Mobile needs work |
| INP | 95ms | 180ms | ✅ Good |
| CLS | 0.05 | 0.15 | ⚠️ Mobile needs work |
## Score Breakdown
| Category | Score | Findings |
|----------|-------|----------|
| Core Web Vitals (CWV) | 6/10 | 1 high, 1 medium |
| Page Load (LOAD) | 7/10 | 1 high |
| Resources (RES) | 5/10 | 2 high, 2 medium |
| Bundle (BUN) | 6/10 | 1 high, 1 medium |
| Runtime (RUN) | 8/10 | 1 medium |
## Resource Waterfall
(Top 10 slowest resources with load times)
## Bundle Breakdown
| Category | Size (gzipped) | Budget | Status |
|----------|---------------|--------|--------|
| First-party JS | 180KB | 250KB | ✅ |
| Third-party JS | 220KB | 150KB | ❌ Over budget |
| CSS | 45KB | 100KB | ✅ |
| Images | 1.2MB | 1.5MB | ✅ |
| Fonts | 85KB | 100KB | ✅ |
## Findings
(structured findings with metrics and evidence)
信頼度スコア
- 90-100%:ブラウザーで具体的な値を測定済み(例:LCP:3.2s)
- 70-89%:リソース分析から導出(例:最適化されていない画像を検出)
- 50-69%:コードレベルのパターン(例:遅延読み込み属性が見つからない)
- 50% 未満:報告しない
フェーズ 5:修正
1. 修正の指針(例)
#### RES-01: Images not using modern formats
**Impact:** ~40% larger images than necessary, adds ~500KB to page weight
**Current:** 8 PNG images totaling 1.2MB
**Fix:** Convert to WebP with fallback:
- Use `<picture>` with WebP source and PNG fallback
- Or use Next.js `<Image>` / `sharp` for automatic format negotiation
- Expected savings: ~480KB (40% reduction)
**Priority files:**
- /images/hero.png (320KB → ~190KB as WebP)
- /images/features.png (280KB → ~165KB as WebP)
2. YAML 回帰テスト
- name: cwv-01-lcp-under-threshold
description: Verify Largest Contentful Paint is under 2.5 seconds
severity: high
standard: Core-Web-Vitals-LCP
steps:
- description: Set up an LCP observer before navigation
js: |
// Set up LCP observer before navigation
await page.evaluateOnNewDocument(() => {
window.__lcp = 0;
new PerformanceObserver((list) => {
const entries = list.getEntries();
window.__lcp = entries[entries.length - 1].startTime;
}).observe({ type: 'largest-contentful-paint', buffered: true });
});
- URL: /
- WAIT_UNTIL: Page has fully loaded including all images and content
timeout_seconds: 30
- description: Assert Largest Contentful Paint is under 2.5s
js: |
const lcp = await page.evaluate(() => window.__lcp);
if (lcp > 2500) {
throw new Error(`LCP is ${lcp}ms, exceeds 2500ms threshold`);
}
console.log(`LCP: ${lcp}ms (threshold: 2500ms)`);
- VERIFY: Page loaded with Largest Contentful Paint under 2.5 seconds
すべての YAML テストを shiplight/tests/performance-review.test.yaml に保存します。
詳細レベル
--quick:メインページの Core Web Vitals のみ。約2分。- デフォルト:主要ページの全カテゴリをデスクトップとモバイルで確認。約8~12分。
--thorough:全カテゴリに加え、追加ページ、統計的信頼性を高めるための複数回の実行、実行時プロファイリングを実施。約20~30分。
ヒント
- 本番環境(または本番に近いビルド)で測定します。開発モードのパフォーマンスは実態を反映しません。
- 複数回実行します。パフォーマンス測定値にはばらつきがあるため、単一のデータ点ではなく傾向を確認します。
- モバイルシミュレーションではデスクトップで見つからない問題が明らかになります。必ず両方をテストします。
performance.getEntries()を使用します。ブラウザーで得られるパフォーマンスデータの中で、最も情報量が豊富です。- まず Core Web Vitals に注目します。Google がランキングに使用する指標です。
close_sessionでセッションを閉じ、証拠の提示にはgenerate_html_reportを使用します。