jpskill.com
📦 その他 コミュニティ

performance-optimization

ウェブサイトやアプリの表示速度改善、パフォーマンス低下時の原因特定、ボトルネック解消など、ビジネス成果に繋がる高速化を実現するSkill。

📜 元の英語説明(参考)

Use when performance requirements exist, when you suspect performance regressions, or when Core Web Vitals or load times need improvement. Use when profiling reveals bottlenecks that need fixing.

🇯🇵 日本人クリエイター向け解説

一言でいうと

ウェブサイトやアプリの表示速度改善、パフォーマンス低下時の原因特定、ボトルネック解消など、ビジネス成果に繋がる高速化を実現するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o performance-optimization.zip https://jpskill.com/download/9586.zip && unzip -o performance-optimization.zip && rm performance-optimization.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9586.zip -OutFile "$d\performance-optimization.zip"; Expand-Archive "$d\performance-optimization.zip" -DestinationPath $d -Force; ri "$d\performance-optimization.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して performance-optimization.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → performance-optimization フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

パフォーマンス最適化

概要

最適化の前に測定を行いましょう。測定なしのパフォーマンス改善は推測に過ぎません。そして、推測は、重要なものを改善することなく複雑さを増す、時期尚早な最適化につながります。まずプロファイルを行い、実際のボトルネックを特定し、それを修正し、再度測定します。測定によって重要だと証明されたものだけを最適化してください。

使用するタイミング

  • パフォーマンス要件が仕様書に存在する(ロード時間の予算、応答時間の SLA)
  • ユーザーまたは監視システムが遅延を報告している
  • Core Web Vitals のスコアが閾値を下回っている
  • 変更によってリグレッションが発生した疑いがある
  • 大量のデータセットまたは高トラフィックを処理する機能を構築している

使用しないタイミング: 問題の証拠がない場合は最適化しないでください。時期尚早な最適化は、得られるパフォーマンスよりもコストのかかる複雑さを追加します。

Core Web Vitals の目標値

メトリクス 良好 改善が必要 不良
LCP (Largest Contentful Paint) ≤ 2.5s ≤ 4.0s > 4.0s
INP (Interaction to Next Paint) ≤ 200ms ≤ 500ms > 500ms
CLS (Cumulative Layout Shift) ≤ 0.1 ≤ 0.25 > 0.25

最適化のワークフロー

1. 測定  → 実際のデータでベースラインを確立する
2. 特定 → 実際のボトルネックを見つける(想定ではなく)
3. 修正  → 特定のボトルネックに対処する
4. 検証   → 再度測定し、改善を確認する
5. 保護   → 監視またはテストを追加して、リグレッションを防ぐ

ステップ 1: 測定

フロントエンド:

# Chrome DevTools の Lighthouse (または CI)
# Chrome DevTools → Performance タブ → Record
# Chrome DevTools MCP → Performance trace

# コード内の Web Vitals ライブラリ
import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(console.log);
onINP(console.log);
onCLS(console.log);

バックエンド:

# 応答時間のログ記録
# Application Performance Monitoring (APM)
# タイミング付きのデータベースクエリのログ記録

# 簡単なタイミング
console.time('db-query');
const result = await db.query(...);
console.timeEnd('db-query');

ステップ 2: ボトルネックの特定

カテゴリ別の一般的なボトルネック:

フロントエンド:

症状 考えられる原因 調査
遅い LCP 大きな画像、レンダリングをブロックするリソース、遅いサーバー ネットワークウォーターフォール、画像サイズを確認
高い CLS 寸法のない画像、遅れてロードされるコンテンツ、フォントのずれ レイアウトシフトのアトリビューションを確認
悪い INP メインスレッドでの重い JavaScript、大きな DOM の更新 Performance trace で長いタスクを確認
遅い初期ロード 大きなバンドル、多くのネットワークリクエスト バンドルサイズ、コード分割を確認

バックエンド:

症状 考えられる原因 調査
遅い API レスポンス N+1 クエリ、インデックスの欠落、最適化されていないクエリ データベースクエリログを確認
メモリの増加 リークした参照、制限のないキャッシュ、大きなペイロード ヒープスナップショット分析
CPU スパイク 同期的な重い計算、正規表現のバックトラッキング CPU プロファイリング
高いレイテンシ キャッシュの欠落、冗長な計算、ネットワークホップ スタック全体のリクエストをトレース

ステップ 3: 一般的なアンチパターンの修正

N+1 クエリ (バックエンド)

// BAD: N+1 — タスクごとに所有者に対する 1 つのクエリ
const tasks = await db.tasks.findMany();
for (const task of tasks) {
  task.owner = await db.users.findUnique({ where: { id: task.ownerId } });
}

// GOOD: join/include を使用した単一のクエリ
const tasks = await db.tasks.findMany({
  include: { owner: true },
});

無制限のデータフェッチ

// BAD: すべてのレコードをフェッチする
const allTasks = await db.tasks.findMany();

// GOOD: 制限付きのページネーション
const tasks = await db.tasks.findMany({
  take: 20,
  skip: (page - 1) * 20,
  orderBy: { createdAt: 'desc' },
});

画像最適化の欠落 (フロントエンド)

<!-- BAD: 寸法なし、遅延ロードなし、レスポンシブサイズなし -->
<img src="/hero.jpg" />

<!-- GOOD: レスポンシブ、遅延ロード、適切なサイズ -->
<img
  src="/hero.jpg"
  srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
  sizes="(max-width: 768px) 100vw, 50vw"
  width="1200"
  height="600"
  loading="lazy"
  alt="Hero image description"
/>

不要な再レンダリング (React)

// BAD: レンダリングごとに新しいオブジェクトを作成し、子を再レンダリングさせる
function TaskList() {
  return <TaskFilters options={{ sortBy: 'date', order: 'desc' }} />;
}

// GOOD: 安定した参照
const DEFAULT_OPTIONS = { sortBy: 'date', order: 'desc' } as const;
function TaskList() {
  return <TaskFilters options={DEFAULT_OPTIONS} />;
}

// コストのかかるコンポーネントには React.memo を使用する
const TaskItem = React.memo(function TaskItem({ task }: Props) {
  return <div>{/* expensive render */}</div>;
});

// コストのかかる計算には useMemo を使用する
function TaskStats({ tasks }: Props) {
  const stats = useMemo(() => calculateStats(tasks), [tasks]);
  return <div>{stats.completed} / {stats.total}</div>;
}

大きなバンドルサイズ

// BAD: ライブラリ全体をインポートする
import { format } from 'date-fns';

// GOOD: ツリーシェイキング可能なインポート (ライブラリがサポートしている場合)
import { format } from 'date-fns/format';

// GOOD: 重く、めったに使用されない機能の動的インポート
const ChartLibrary = lazy(() => import('./ChartLibrary'));

キャッシュの欠落 (バックエンド)

// 頻繁に読み取られ、めったに変更されないデータをキャッシュする
const CACHE_TTL = 5 * 60 * 1000; // 5 分
let cachedConfig: AppConfig | null = null;
let cacheExpiry = 0;

async function getAppConfig(): Promise<AppConfig> {
  if (cachedConfig && Date.now() < cacheExpiry) {
    return cachedConfig;
  }
  cachedConfig = await db.config.findFirst();
  cacheExpiry = Date.now() + CACHE_TTL;
  return cachedConfig;
}

// 静的アセットの HTTP キャッシュヘッダー
app.use('/static', express.static('public', {
  maxAge: '1y',           // 1 年間キャッシュ
  immutable: true,        // 再検証しない (ファイル名でコンテンツハッシュを使用)
}));

// API レスポンスの Cache-Control
res.set('Cache-Control', 'public, max-age=300'); // 5 分

パフォーマンス予算

S

📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Performance Optimization

Overview

Measure before optimizing. Performance work without measurement is guessing — and guessing leads to premature optimization that adds complexity without improving what matters. Profile first, identify the actual bottleneck, fix it, measure again. Optimize only what measurements prove matters.

When to Use

  • Performance requirements exist in the spec (load time budgets, response time SLAs)
  • Users or monitoring report slow behavior
  • Core Web Vitals scores are below thresholds
  • You suspect a change introduced a regression
  • Building features that handle large datasets or high traffic

When NOT to use: Don't optimize before you have evidence of a problem. Premature optimization adds complexity that costs more than the performance it gains.

Core Web Vitals Targets

Metric Good Needs Improvement Poor
LCP (Largest Contentful Paint) ≤ 2.5s ≤ 4.0s > 4.0s
INP (Interaction to Next Paint) ≤ 200ms ≤ 500ms > 500ms
CLS (Cumulative Layout Shift) ≤ 0.1 ≤ 0.25 > 0.25

The Optimization Workflow

1. MEASURE  → Establish baseline with real data
2. IDENTIFY → Find the actual bottleneck (not assumed)
3. FIX      → Address the specific bottleneck
4. VERIFY   → Measure again, confirm improvement
5. GUARD    → Add monitoring or tests to prevent regression

Step 1: Measure

Frontend:

# Lighthouse in Chrome DevTools (or CI)
# Chrome DevTools → Performance tab → Record
# Chrome DevTools MCP → Performance trace

# Web Vitals library in code
import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(console.log);
onINP(console.log);
onCLS(console.log);

Backend:

# Response time logging
# Application Performance Monitoring (APM)
# Database query logging with timing

# Simple timing
console.time('db-query');
const result = await db.query(...);
console.timeEnd('db-query');

Step 2: Identify the Bottleneck

Common bottlenecks by category:

Frontend:

Symptom Likely Cause Investigation
Slow LCP Large images, render-blocking resources, slow server Check network waterfall, image sizes
High CLS Images without dimensions, late-loading content, font shifts Check layout shift attribution
Poor INP Heavy JavaScript on main thread, large DOM updates Check long tasks in Performance trace
Slow initial load Large bundle, many network requests Check bundle size, code splitting

Backend:

Symptom Likely Cause Investigation
Slow API responses N+1 queries, missing indexes, unoptimized queries Check database query log
Memory growth Leaked references, unbounded caches, large payloads Heap snapshot analysis
CPU spikes Synchronous heavy computation, regex backtracking CPU profiling
High latency Missing caching, redundant computation, network hops Trace requests through the stack

Step 3: Fix Common Anti-Patterns

N+1 Queries (Backend)

// BAD: N+1 — one query per task for the owner
const tasks = await db.tasks.findMany();
for (const task of tasks) {
  task.owner = await db.users.findUnique({ where: { id: task.ownerId } });
}

// GOOD: Single query with join/include
const tasks = await db.tasks.findMany({
  include: { owner: true },
});

Unbounded Data Fetching

// BAD: Fetching all records
const allTasks = await db.tasks.findMany();

// GOOD: Paginated with limits
const tasks = await db.tasks.findMany({
  take: 20,
  skip: (page - 1) * 20,
  orderBy: { createdAt: 'desc' },
});

Missing Image Optimization (Frontend)

<!-- BAD: No dimensions, no lazy loading, no responsive sizes -->
<img src="/hero.jpg" />

<!-- GOOD: Responsive, lazy-loaded, properly sized -->
<img
  src="/hero.jpg"
  srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
  sizes="(max-width: 768px) 100vw, 50vw"
  width="1200"
  height="600"
  loading="lazy"
  alt="Hero image description"
/>

Unnecessary Re-renders (React)

// BAD: Creates new object on every render, causing children to re-render
function TaskList() {
  return <TaskFilters options={{ sortBy: 'date', order: 'desc' }} />;
}

// GOOD: Stable reference
const DEFAULT_OPTIONS = { sortBy: 'date', order: 'desc' } as const;
function TaskList() {
  return <TaskFilters options={DEFAULT_OPTIONS} />;
}

// Use React.memo for expensive components
const TaskItem = React.memo(function TaskItem({ task }: Props) {
  return <div>{/* expensive render */}</div>;
});

// Use useMemo for expensive computations
function TaskStats({ tasks }: Props) {
  const stats = useMemo(() => calculateStats(tasks), [tasks]);
  return <div>{stats.completed} / {stats.total}</div>;
}

Large Bundle Size

// BAD: Importing entire library
import { format } from 'date-fns';

// GOOD: Tree-shakable import (if the library supports it)
import { format } from 'date-fns/format';

// GOOD: Dynamic import for heavy, rarely-used features
const ChartLibrary = lazy(() => import('./ChartLibrary'));

Missing Caching (Backend)

// Cache frequently-read, rarely-changed data
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
let cachedConfig: AppConfig | null = null;
let cacheExpiry = 0;

async function getAppConfig(): Promise<AppConfig> {
  if (cachedConfig && Date.now() < cacheExpiry) {
    return cachedConfig;
  }
  cachedConfig = await db.config.findFirst();
  cacheExpiry = Date.now() + CACHE_TTL;
  return cachedConfig;
}

// HTTP caching headers for static assets
app.use('/static', express.static('public', {
  maxAge: '1y',           // Cache for 1 year
  immutable: true,        // Never revalidate (use content hashing in filenames)
}));

// Cache-Control for API responses
res.set('Cache-Control', 'public, max-age=300'); // 5 minutes

Performance Budget

Set budgets and enforce them:

JavaScript bundle: < 200KB gzipped (initial load)
CSS: < 50KB gzipped
Images: < 200KB per image (above the fold)
Fonts: < 100KB total
API response time: < 200ms (p95)
Time to Interactive: < 3.5s on 4G
Lighthouse Performance score: ≥ 90

Enforce in CI:

# Bundle size check
npx bundlesize --config bundlesize.config.json

# Lighthouse CI
npx lhci autorun

Common Rationalizations

Rationalization Reality
"We'll optimize later" Performance debt compounds. Fix obvious anti-patterns now, defer micro-optimizations.
"It's fast on my machine" Your machine isn't the user's. Profile on representative hardware and networks.
"This optimization is obvious" If you didn't measure, you don't know. Profile first.
"Users won't notice 100ms" Research shows 100ms delays impact conversion rates. Users notice more than you think.
"The framework handles performance" Frameworks prevent some issues but can't fix N+1 queries or oversized bundles.

Red Flags

  • Optimization without profiling data to justify it
  • N+1 query patterns in data fetching
  • List endpoints without pagination
  • Images without dimensions, lazy loading, or responsive sizes
  • Bundle size growing without review
  • No performance monitoring in production
  • React.memo and useMemo everywhere (overusing is as bad as underusing)

Verification

After any performance-related change:

  • [ ] Before and after measurements exist (specific numbers)
  • [ ] The specific bottleneck is identified and addressed
  • [ ] Core Web Vitals are within "Good" thresholds
  • [ ] Bundle size hasn't increased significantly
  • [ ] No N+1 queries in new data fetching code
  • [ ] Performance budget passes in CI (if configured)
  • [ ] Existing tests still pass (optimization didn't break behavior)