debugging について
debuggingは、実行時エラーや予期しない挙動、ビルド問題を調べる開発者向けの、GoodVibes用デバッグスキルです。再現、切り分け、根本原因の特定、修正、検証の手順を示し、ネットワークや状態の調査も扱います。
主な機能
- 01段階的なデバッグ手順
- 02エラー情報とスタックトレースの分析
- 03データフローと境界条件の追跡
- 04依存関係、インポート、型の確認
- 05型チェックとテストによる修正検証
- 06ネットワークと状態の問題調査
活用シーン
- 01TypeScriptの型エラーの特定
- 02実行時例外やロジック不具合の調査
- 03APIリクエストとレスポンスの障害分析
- 04ビルドエラーや性能上のボトルネックの発見
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mgd34msu/ goodvibes-plugin --skill debuggingインストール後、Claude Code で/debuggingを入力して読み込まれたか確認します。
同梱ファイルreferences/debugging-patterns.mdscripts/validate-debugging.sh
詳細
リソース
scripts/
validate-debugging.sh
references/
debugging-patterns.md
デバッグ品質スキル
このスキルでは、GoodVibes の高精度ツールを使って体系的にデバッグする方法を学びます。効果的なデバッグには、問題を再現し、原因を切り分け、根本的な問題を特定し、修正を適用して、解決策を検証するという手順が必要です。
このスキルを使用する場面
次のような場合に、このスキルを読み込んでください。
- ランタイムエラーや例外を調査するとき
- 予期しない動作やロジックエラーを追跡するとき
- ネットワークリクエスト/レスポンスの問題を分析するとき
- 状態管理の問題をデバッグするとき
- パフォーマンスのボトルネックを診断するとき
- ビルドエラーやコンパイルエラーを調査するとき
- TypeScript の型エラーを理解するとき
- 根本原因分析を実施するとき
トリガーフレーズ:「このエラーをデバッグして」「なぜ失敗するのか」「問題を調査して」「問題を追跡して」「エラーを分析して」。
基本的なワークフロー
フェーズ 1:再現 — 問題を確認する
デバッグを始める前に、問題を確実に再現できることを確認します。
ステップ 1.1:エラーのコンテキストを収集する
利用可能なエラー情報をすべて収集します。
precision_exec:
commands:
- cmd: "npm run typecheck 2>&1"
- cmd: "npm run lint 2>&1"
- cmd: "npm run build 2>&1"
verbosity: standard
記録する情報:
- エラーメッセージとスタックトレース全体
- エラーの種類とコード(該当する場合)
- ファイルパスと行番号
- タイムスタンプと環境情報
- エラーを引き起こしたユーザー操作
ステップ 1.2:エラーの発生箇所を探す
コードベース内で、類似するエラーを検索します。
discover:
queries:
- id: error_throw_sites
type: grep
pattern: "throw new (Error|TypeError|RangeError)"
glob: "**/*.{ts,tsx,js,jsx}"
- id: error_handlers
type: grep
pattern: "catch\\s*\\("
glob: "**/*.{ts,tsx,js,jsx}"
- id: console_errors
type: grep
pattern: "console\\.(error|warn)"
glob: "**/*.{ts,tsx,js,jsx}"
verbosity: locations
パターンのカテゴリ:
| エラーパターン | 意味 | よくある原因 |
|---|---|---|
| ReferenceError | 変数が定義されていない | タイプミス、インポート漏れ、スコープの問題 |
| TypeError | 誤った型が使用されている | null/undefined、誤ったメソッド、型の不一致 |
| RangeError | 値が範囲外 | 配列インデックス、数値のオーバーフロー |
| SyntaxError | 構文が無効 | パースエラー、不正な形式の JSON |
| NetworkError | リクエストに失敗した | CORS、タイムアウト、404、認証の失敗 |
ステップ 1.3:ローカルで再現する
失敗する操作を実行し、再現できることを確認します。
precision_exec:
commands:
- cmd: "npm run dev"
timeout_ms: 5000
verbosity: minimal
再現チェックリスト:
- エラーが一貫して再現する
- 再現するための最小限の手順を特定した
- 環境が本番環境と一致している(Node のバージョン、依存関係)
- エラーメッセージが報告された問題と一致している
フェーズ 2:切り分け — 原因を絞り込む
問題の範囲を狭め、エラーの発生元を見つけます。
ステップ 2.1:スタックトレースを読む
コールスタックを分析し、実行フローを理解します。
スタックトレースの例:
TypeError: Cannot read property 'name' of undefined
at getUserName (src/utils/user.ts:42:18)
at UserProfile (src/components/UserProfile.tsx:15:22)
at renderWithHooks (node_modules/react/cjs/react.development.js:1234)
読み解く手順:
- 先頭から確認する — 実際のエラー箇所
- さかのぼって追跡する — 呼び出しチェーンをたどる
- node_modules は無視する — 自分のコードに注目する
- エントリーポイントを見つける — 不正なデータはどこから来たのか?
precision_read:
files:
- path: "src/utils/user.ts"
extract: content
range: {start: 35, end: 50}
- path: "src/components/UserProfile.tsx"
extract: content
range: {start: 10, end: 25}
verbosity: standard
ステップ 2.2:データフローを追跡する
データをソースからエラー箇所までたどります。
discover:
queries:
- id: function_calls
type: grep
pattern: "getUserName\\("
glob: "**/*.{ts,tsx}"
- id: data_sources
type: grep
pattern: "(fetch|axios|prisma).*user"
glob: "**/*.{ts,tsx}"
- id: prop_passing
type: grep
pattern: "user=\\{"
glob: "**/*.tsx"
verbosity: locations
データフローの分析:
- データはどこから来るのか?(API、データベース、props)
- どのような変換が行われるのか?(map、filter、分割代入)
- どのような前提があるのか?(null ではない、特定の形状である)
- どこで undefined になる可能性があるのか?
ステップ 2.3:境界条件を確認する
エラーがよく発生するエッジケースをテストします。
discover:
queries:
- id: array_access
type: grep
pattern: "\\[[0-9]+\\]|\\[.*\\]"
glob: "**/*.{ts,tsx}"
- id: null_checks
type: grep
pattern: "(if.*===.*null|if.*===.*undefined|\\?\\.|\\?\\?)"
glob: "**/*.{ts,tsx}"
- id: optional_chaining
type: grep
pattern: "\\?\\."
glob: "**/*.{ts,tsx}"
verbosity: locations
よくある境界条件の問題:
| 問題 | 例 | 修正方法 |
|---|---|---|
| 配列の範囲外 | arr[arr.length] | arr.length > 0 を確認する |
| null/undefined へのアクセス | user.name | user?.name を使用する |
| ゼロ除算 | total / count | count !== 0 を確認する |
| 空文字列に対する操作 | str[0] | str.length > 0 を確認する |
| オブジェクトキーの欠落 | obj.key | obj?.key ?? default を使用する |
フェーズ 3:特定 — 根本原因を突き止める
症状だけでなく、根本的な問題を特定します。
ステップ 3.1:エラーパターンを分析する
よくある原因を把握するために、エラーを分類します。
型エラー
precision_exec:
commands:
- cmd: "npm run typecheck 2>&1 | head -50"
verbosity: standard
よくある TypeScript エラー:
| エラーコード | 意味 | 修正方法 |
|---|---|---|
| TS2339 | プロパティが存在しません | プロパティを型に追加するか、オプショナルチェーンを使用します |
| TS2345 | 引数の型が一致しません | 型を修正するか、型アサーションを追加します |
| TS2322 | 型を割り当てられません | 型を変更するか、値を修正します |
| TS2571 | オブジェクトの型が unknown です | 型ガードまたは型アサーションを追加します |
| TS7006 | パラメーターの型が暗黙的に any です | パラメーターの型注釈を追加します |
実行時エラー
discover:
queries:
- id: unsafe_access
type: grep
pattern: "\\.[a-zA-Z]+(?!\\?)"
glob: "**/*.{ts,tsx}"
- id: async_errors
type: grep
pattern: "await.*(?!try)"
glob: "**/*.{ts,tsx}"
verbosity: files_only
よくある実行時エラー:
- undefined のプロパティ X を読み取れません
- null のプロパティ X を読み取れません
- X は関数ではありません
- X は反復可能ではありません
- コールスタックの最大サイズを超過しました(無限再帰)
ステップ 3.2:依存関係とインポートを確認する
モジュール解決エラーはよく発生します。
discover:
queries:
- id: imports
type: grep
pattern: "^import.*from"
glob: "**/*.{ts,tsx}"
- id: dynamic_imports
type: grep
pattern: "import\\("
glob: "**/*.{ts,tsx}"
- id: require_statements
type: grep
pattern: "require\\("
glob: "**/*.{ts,js}"
verbosity: locations
よくあるインポートの問題:
| 問題 | 症状 | 修正方法 |
|---|---|---|
| エクスポートがない | モジュールにエクスポートされたメンバーがありません | ソースファイルにエクスポートを追加します |
| 循環依存 | 初期化前にアクセスできません | インポートの構成を変更します |
| パスが間違っている | モジュールが見つかりません | 相対パスまたはエイリアスを修正します |
| デフォルトと名前付きの違い | X は関数ではありません | import X または import { X } を使用します |
| 型のみのインポート | 値として使用できません | type キーワードを削除します |
ステップ 3.3:前提を検証する
データの形状や状態に関する前提を見直します。
precision_grep:
queries:
- id: type_assertions
pattern: "as (unknown|any|[A-Z][a-zA-Z]+)"
glob: "**/*.{ts,tsx}"
output:
format: context
context_before: 2
context_after: 2
verbosity: standard
危険な前提:
- API は常に期待どおりの形状でデータを返す
- 配列が空になることはない
- ユーザーは常に認証済みである
- データは常に有効である
- Props は常に渡される
次の方法で検証します:
- 実行時の型検証(Zod、Yup)
- 型ガード(
if (typeof x === 'string')) - null チェック(
if (x != null)) - デフォルト値(
x ?? defaultValue)
フェーズ 4:修正 - 解決策を適用する
根本原因の分析に基づいて修正を実装します。
ステップ 4.1:適切な修正方法を選ぶ
問題の種類に合った修正方法を選びます。
型安全性の修正
// Before: Implicit any
function getUser(id) {
return users.find(u => u.id === id);
}
// After: Explicit types
function getUser(id: string): User | undefined {
return users.find(u => u.id === id);
}
null 安全性の修正
// Before: Unsafe access
const name = user.profile.name;
// After: Optional chaining
const name = user?.profile?.name ?? 'Anonymous';
非同期エラー処理
// Before: Unhandled promise
await fetchData();
// After: Try/catch
try {
await fetchData();
} catch (error: unknown) {
logger.error('Failed to fetch data', { error });
throw new AppError('Data fetch failed', { cause: error });
}
配列の境界に関する修正
// Before: Unsafe access
const first = items[0];
// After: Safe access
const first = items.length > 0 ? items[0] : null;
// Or: const first = items.at(0) ?? null;
ステップ 4.2:防御的なコードを追加する
同様のエラーが今後発生しないようにします。
入力検証:
import { z } from 'zod';
const userSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1).max(100),
});
function processUser(data: unknown) {
const user = userSchema.parse(data); // Throws if invalid
// Now user is type-safe
}
型ガード:
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'email' in value
);
}
if (isUser(data)) {
// TypeScript knows data is User
logger.info('User email', { email: data.email });
}
エラーバウンダリ(React):
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error }: { error: Error }) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
</div>
);
}
<ErrorBoundary FallbackComponent={ErrorFallback}>
<MyComponent />
</ErrorBoundary>
ステップ 4.3:修正を適用する
precision_edit を使用して変更します。
precision_edit:
edits:
- path: "src/utils/user.ts"
find: |
function getUserName(user) {
return user.name;
}
replace: |
function getUserName(user: User | undefined): string {
return user?.name ?? 'Unknown';
}
verbosity: minimal
フェーズ 5:検証 - 修正が機能することを確認する
修正によって問題が解決し、ほかに問題が生じていないことを検証します。
ステップ 5.1:型チェック
TypeScript のエラーが解消されていることを確認します。
precision_exec:
commands:
- cmd: "npm run typecheck"
verbosity: standard
すべての型エラーを解消する必要があります。
ステップ 5.2:テストを実行する
テストが成功することを確認します。
precision_exec:
commands:
- cmd: "npm test"
verbosity: standard
テストが失敗した場合:
- 修正によってリグレッションが発生した
- テストの期待値を更新する必要がある
- テストによって別の問題が見つかった
ステップ 5.3:手動で検証する
元のエラーが発生した状況を再現します。
precision_exec:
commands:
- cmd: "npm run dev"
timeout_ms: 5000
verbosity: minimal
検証チェックリスト:
- 元のエラーが再発しない
- エッジケースに対応している(null、空、大きな値)
- 新たなエラーが発生していない
- パフォーマンスが低下していない
- ユーザーエクスペリエンスが向上している
フェーズ 6:ネットワークのデバッグ
API リクエストとレスポンスをデバッグします。
ステップ 6.1:ネットワーク呼び出しを見つける
すべての HTTP リクエストを特定します。
discover:
queries:
- id: fetch_calls
type: grep
pattern: "fetch\\("
glob: "**/*.{ts,tsx,js,jsx}"
- id: axios_calls
type: grep
pattern: "axios\\.(get|post|put|delete|patch)"
glob: "**/*.{ts,tsx,js,jsx}"
- id: api_routes
type: glob
patterns: ["src/app/api/**/*.ts", "pages/api/**/*.ts"]
verbosity: locations
ステップ 6.2:リクエスト/レスポンスの処理を確認する
ネットワーク呼び出しのエラー処理を検証します。
precision_grep:
queries:
- id: fetch_with_catch
pattern: "fetch\\([^)]+\\).*catch"
glob: "**/*.{ts,tsx,js,jsx}"
output:
format: context
context_after: 5
verbosity: standard
よくあるネットワークの問題:
| 問題 | 症状 | 修正方法 |
|---|---|---|
| CORS エラー | CORS ポリシーによってブロックされた | API に CORS ヘッダーを追加します |
| 401 Unauthorized | トークンがないか無効 | 認証ヘッダーを追加し、トークンを更新します |
| 404 Not Found | URL またはルートが間違っている | エンドポイントのパスを修正します |
| 500 Server Error | バックエンドの例外 | サーバーログを確認し、バックエンドを修正します |
| タイムアウト | リクエストに時間がかかりすぎる | タイムアウトを延長し、バックエンドを最適化します |
| ネットワーク障害 | フェッチに失敗した | 接続を確認し、再試行ロジックを追加します |
CORS のデバッグ:
// Next.js API route
export async function GET(request: Request) {
return Response.json(data, {
headers: {
'Access-Control-Allow-Origin': '*', // Development only - use specific origin in production
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
認証のデバッグ:
// Check for auth header
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const session = await verifyToken(token);
} catch (error: unknown) {
logger.error('Token verification failed', { error });
return Response.json({ error: 'Invalid token' }, { status: 401 });
}
ステップ 6.3:リクエストペイロードを検証する
リクエストデータが正しいことを確認します。
discover:
queries:
- id: request_bodies
type: grep
pattern: "(body:|JSON.stringify)"
glob: "**/*.{ts,tsx}"
- id: validation_schemas
type: grep
pattern: "z\\.object\\("
glob: "**/*.{ts,tsx}"
verbosity: locations
リクエストの検証:
import { z } from 'zod';
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export async function POST(request: Request) {
const body = await request.json();
const result = createUserSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ error: 'Validation failed', details: result.error.flatten() },
{ status: 400 }
);
}
// Now result.data is type-safe
}
フェーズ 7:状態のデバッグ
React の状態と状態管理をデバッグします。
ステップ 7.1:状態の使用箇所を見つける
状態の宣言と更新箇所を特定します。
discover:
queries:
- id: useState_calls
type: grep
pattern: "useState<?"
glob: "**/*.{tsx,jsx}"
- id: state_updates
type: grep
pattern: "set[A-Z][a-zA-Z]*\\("
glob: "**/*.{tsx,jsx}"
- id: useEffect_hooks
type: grep
pattern: "useEffect\\("
glob: "**/*.{tsx,jsx}"
verbosity: locations
よくある状態の問題:
| 問題 | 症状 | 修正方法 |
|---|---|---|
| 古いクロージャ | 状態の値が古い | 関数形式の更新を使用する |
| 依存関係の不足 | useEffect が再実行されない | 依存配列に追加する |
| 無限ループ | コンポーネントが延々と再レンダリングされる | 依存関係または条件を修正する |
| 状態の競合状態 | 更新が互いに上書きされる | reducer またはキューを使用する |
| アンマウント時の状態消失 | データが消える | 状態を上位に移動するか、グローバル状態を使用する |
古いクロージャの修正:
// Before: Stale closure
const [count, setCount] = useState(0);
function increment() {
setTimeout(() => {
setCount(count + 1); // count is stale
}, 1000);
}
// After: Functional update
function increment() {
setTimeout(() => {
setCount(prev => prev + 1); // Always current
}, 1000);
}
useEffect の依存関係の修正:
// Before: Missing dependency
useEffect(() => {
fetchData(userId);
}, []); // userId changes ignored
// After: Complete dependencies
useEffect(() => {
fetchData(userId);
}, [userId]); // Re-runs when userId changes
ステップ 7.2:状態の変更を確認する
状態を直接変更している箇所(アンチパターン)を見つけます。
discover:
queries:
- id: array_mutations
type: grep
pattern: "\\.(push|pop|shift|unshift|splice)\\("
glob: "**/*.{tsx,jsx}"
- id: object_mutations
type: grep
pattern: "[a-zA-Z]+\\.[a-zA-Z]+\\s*=\\s*"
glob: "**/*.{tsx,jsx}"
verbosity: locations
不変な更新:
// Before: Mutation
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // BAD: Mutates state
setItems(items);
// After: Immutable
setItems([...items, 4]); // GOOD: New array
// Object updates
const [user, setUser] = useState({ name: 'Alice', age: 30 });
setUser({ ...user, age: 31 }); // GOOD: New object
フェーズ 8:パフォーマンスのデバッグ
パフォーマンスのボトルネックを診断します。
ステップ 8.1:パフォーマンスのアンチパターンを見つける
よくあるパフォーマンスの問題を検索します。
discover:
queries:
- id: n_plus_one
type: grep
pattern: "(for|forEach|map).*await.*(prisma|db|query)"
glob: "**/*.{ts,tsx}"
- id: inline_objects
type: grep
pattern: "(onClick|onChange|style)=\\{\\{"
glob: "**/*.{tsx,jsx}"
- id: missing_memo
type: grep
pattern: "(map|filter|reduce|sort)\\("
glob: "**/*.{tsx,jsx}"
verbosity: locations
パフォーマンスの問題:
| アンチパターン | 影響 | 修正方法 |
|---|---|---|
| N+1 クエリ | データベースクエリが遅くなる | include またはバッチ読み込みを使用する |
| JSX 内のインラインオブジェクト | 不要な再レンダリング | 定数に切り出すか、useMemo を使用する |
| 仮想化されていない大規模なリスト | レンダリングが遅くなる | react-window などを使用する |
| インデックスの不足 | クエリが遅くなる | データベースインデックスを追加する |
| 不要な再レンダリング | UI の動作が遅くなる | React.memo、useMemo、useCallback を使用する |
ステップ 8.2:ビルドのパフォーマンスをプロファイリングする
ビルドが遅い原因を確認します。
precision_exec:
commands:
- cmd: "npm run build"
verbosity: standard
ビルドのパフォーマンスの問題:
- バンドルサイズが大きい(バンドルアナライザーで確認)
- TypeScript のコンパイルが遅い(tsconfig を確認)
- ツリーシェイキングが機能していない(インポートを確認)
- 本番環境に開発用依存関係が含まれている(package.json を確認)
フェーズ 9:根本原因の分析
症状だけでなく、根本的な問題を見つけます。
ステップ 9.1:5 回「なぜ」を問う
根本原因まで掘り下げます。
例:
- なぜアプリがクラッシュしたのか? - ユーザーデータが undefined だった
- なぜユーザーデータが undefined だったのか? - API が null を返した
- なぜ API が null を返したのか? - データベースクエリが行を返さなかった
- なぜ行がなかったのか? - ユーザー ID が正しくなかった
- なぜユーザー ID が正しくなかったのか? - ルートパラメーターの解析に失敗した
根本原因: ルートパラメーターの解析がエッジケースに対応していません。
ステップ 9.2:システムの依存関係を確認する
環境と依存関係を検証します。
precision_exec:
commands:
- cmd: "node --version"
- cmd: "npm --version"
- cmd: "npm list --depth=0"
verbosity: minimal
よくある依存関係の問題:
- バージョンの不一致(package.json とロックファイル)
- ピア依存関係の競合
- 既知のバグがある古いパッケージ
- ネイティブ依存関係の不足
ステップ 9.3:最近の変更を確認する
エラーが発生する前に何が変更されたかを特定します。
precision_exec:
commands:
- cmd: "git log --oneline -10"
- cmd: "git diff HEAD~5..HEAD"
verbosity: standard
複雑な問題に対する Git bisect:
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Test, then mark:
git bisect good # or bad
# Repeat until culprit commit found
フェーズ 10:予防 — 今後の問題を回避する
再発を防ぐための安全策を追加します。
ステップ 10.1:テストを追加する
エラーを検出するテストを作成します。
import { describe, it, expect } from 'vitest';
describe('getUserName', () => {
it('should handle undefined user', () => {
expect(getUserName(undefined)).toBe('Unknown');
});
it('should handle user without name', () => {
expect(getUserName({ id: '123' })).toBe('Unknown');
});
it('should return user name', () => {
expect(getUserName({ id: '123', name: 'Alice' })).toBe('Alice');
});
});
ステップ 10.2:ログを追加する
今後のデバッグに役立つよう、重要な判断箇所をログに記録します。
import { logger } from '@/lib/logger';
function processUser(user: User | undefined) {
if (!user) {
logger.warn('User is undefined in processUser');
return null;
}
logger.info('Processing user', { userId: user.id });
try {
// Process user
} catch (error: unknown) {
logger.error('Failed to process user', { userId: user.id, error });
throw error;
}
}
ログ記録のベストプラクティス:
- 構造化ログ(JSON)を使用する
- コンテキスト(ユーザー ID、リクエスト ID)を含める
- 適切なログレベル(error、warn、info、debug)を使用する
- 機密データ(パスワード、トークン)は決してログに記録しない
- リクエストを追跡するために相関 ID を使用する
ステップ 10.3:モニタリングを追加する
本番環境でエラーを追跡します。
エラー追跡(Sentry、Rollbar):
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1,
beforeSend(event, hint) {
// Filter sensitive data
if (event.request) {
delete event.request.cookies;
}
return event;
},
});
カスタムエラークラス:
export class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500,
public context?: Record<string, unknown>
) {
super(message);
this.name = 'AppError';
}
}
throw new AppError('User not found', 'USER_NOT_FOUND', 404, { userId });
よくあるデバッグパターン
カテゴリ別に整理された詳細なパターンについては、references/debugging-patterns.md を参照してください。
エラーパターンの検出
discover を使用すると、複数のエラーパターンを一度に見つけられます。
discover:
queries:
# Type safety issues
- { id: any_usage, type: grep, pattern: ':\\s*any', glob: '**/*.{ts,tsx}' }
- { id: type_assertions, type: grep, pattern: 'as (any|unknown)', glob: '**/*.{ts,tsx}' }
# Runtime safety
- { id: unsafe_access, type: grep, pattern: '\\.[a-zA-Z]+(?!\\?)', glob: '**/*.{ts,tsx}' }
- { id: unhandled_promises, type: grep, pattern: 'await.*(?!try)', glob: '**/*.{ts,tsx}' }
# Performance
- { id: n_plus_one, type: grep, pattern: 'for.*await.*prisma', glob: '**/*.{ts,tsx}' }
- { id: inline_objects, type: grep, pattern: 'onClick=\\{\\{', glob: '**/*.{tsx,jsx}' }
verbosity: locations
系統的なエラー分析
ステップ 1:エラーを分類する
- 型エラー(コンパイル時)
- ランタイムエラー(null、undefined、型の不一致)
- ロジックエラー(誤った動作が発生し、例外は発生しない)
- パフォーマンスエラー(処理が遅すぎる)
- ネットワークエラー(API の失敗)
ステップ 2:類似するパターンを探す
- エラーメッセージを検索する
- エラーの種類を検索する
- 失敗している関数名を検索する
ステップ 3:データを追跡する
- データはどこから来ていますか?
- どのような変換が行われますか?
- どこで失敗しますか?
ステップ 4:前提を検証する
- データの形状は正しいですか?
- 型は正確ですか?
- エッジケースに対応していますか?
ステップ 5:修正して検証する
- 修正を適用する
- 型チェックを実行する
- テストを実行する
- 手動で検証する
デバッグ用の高精度ツール
Discover ツール
エラーパターンを見つけるため、並列検索を実行します。
discover:
queries:
- id: error_sites
type: grep
pattern: "throw new"
glob: '**/*.{ts,tsx,js,jsx}'
- id: catch_blocks
type: grep
pattern: "catch\\s*\\("
glob: '**/*.{ts,tsx,js,jsx}'
- id: console_logs
type: grep
pattern: "console\\.(log|error|warn)"
glob: '**/*.{ts,tsx,js,jsx}'
verbosity: locations
Precision Grep
周辺のコードを理解するため、コンテキストを含めて検索します。
precision_grep:
queries:
- id: error_context
pattern: "TypeError|ReferenceError|RangeError"
glob: "**/*.{ts,tsx,js,jsx}"
output:
format: context
context_before: 5
context_after: 5
verbosity: standard
Precision Exec
診断コマンドを実行します。
precision_exec:
commands:
- cmd: "npm run typecheck 2>&1"
- cmd: "npm run lint 2>&1"
- cmd: "npm test 2>&1"
verbosity: standard
Precision Edit
修正をアトミックに適用します。
precision_edit:
edits:
- path: "src/utils/user.ts"
find: "user.name"
replace: "user?.name ?? 'Unknown'"
verbosity: minimal
検証スクリプト
デバッグの実践内容を検証するには、scripts/validate-debugging.sh を使用します。
./scripts/validate-debugging.sh /path/to/project
スクリプトのチェック項目:
- 本番環境から console.log 文が削除されている
- エラーバウンダリが実装されている
- ソースマップが設定されている
- 適切なロギングライブラリが使用されている
- try/catch ブロックでエラーが処理されている
- デバッグ用の依存関係が本番環境から除外されている
クイックリファレンス
デバッグチェックリスト
再現する:
- エラーが一貫して再現する
- 最小限の再現手順を特定している
- 環境が一致している
- エラーメッセージを記録している
切り分ける:
- スタックトレースを分析している
- データフローを追跡している
- 境界条件をテストしている
- 類似するパターンを見つけている
特定する:
- エラーを分類している
- 根本原因を特定している(症状ではない)
- 前提を検証している
- 依存関係を確認している
修正する:
- 適切な修正方法を選んでいる
- 防御的なコードを追加している
- 修正を適用している
- 新たな問題が発生していない
検証する:
- 型チェックに合格する
- テストに合格する
- 手動での検証が完了している
- エッジケースをテストしている
防止する:
- この問題のテストを追加している
- ログを追加している
- モニタリングを設定している
- ドキュメントを更新している
エラーの重大度ガイド
クリティカル(直ちに修正):
- アプリケーションがクラッシュする
- データの損失または破損
- セキュリティ上の脆弱性
- 本番環境の停止
高(早急に修正):
- 機能が完全に動作しない
- ユーザーエクスペリエンスが低下している
- パフォーマンスが低下している
- 型安全性に違反している
中(可能なときに修正):
- エッジケースでの失敗
- 軽微な UX の問題
- コンソール警告
- エラー処理の欠落
低(いずれ修正):
- コードスタイルの問題
- ドキュメントの不足
- 最適化の余地
- リファクタリングの必要性
避けるべきよくある間違い
原因ではなく症状を修正する:
- 悪い例:値が null である理由を理解せずに null チェックを追加する
- 良い例:値が null である理由を追跡し、発生源を修正する
検証を省略する:
- 悪い例:修正を適用して、動作すると決めつける
- 良い例:テスト、型チェック、手動での検証を実行する
エッジケースを無視する:
- 悪い例:一般的なケースだけを修正する
- 良い例:null、undefined、空の値、大きな値をテストする
再発防止をしない:
- 悪い例:バグを修正して、そのまま先に進む
- 良い例:再発を防ぐためにテストとログを追加する
高度なテクニック
二分探索デバッグ
複雑な問題では、二分探索を使って範囲を絞り込みます。
コードの半分をコメントアウトする:
- 関数の半分をコメントアウトする
- エラーが続く場合、問題は残りの半分にあります
- エラーが消える場合、問題はコメントアウトした部分にあります
- 問題を切り分けられるまで繰り返す
ラバーダックデバッグ
問題を声に出して説明します:
- コードが何をすべきか説明する
- 実際に何をしているか説明する
- 1 行ずつ追っていく
- 説明しているうちに問題が明らかになることがよくあります
タイムトラベルデバッグ
git を使って、バグがいつ導入されたかを特定します:
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Test each commit
git bisect good # or bad
ロギング戦略
重要な値を追跡する:
function processData(data: unknown) {
logger.debug('Input data', { data });
const validated = schema.parse(data);
logger.debug('Validated data', { validated });
const transformed = transform(validated);
logger.debug('Transformed data', { transformed });
return transformed;
}
相関 ID を使用する:
import { v4 as uuid } from 'uuid';
export async function POST(request: Request) {
const requestId = uuid();
logger.info('Request started', { requestId });
try {
const result = await processRequest(request);
logger.info('Request completed', { requestId });
return Response.json(result);
} catch (error: unknown) {
logger.error('Request failed', { requestId, error });
throw error;
}
}
他のスキルとの連携
- 自動修正を試みるには error-recovery を使用します
- レビュー中のエラーを検出するには code-review を使用します
- リグレッションを防ぐには testing-strategy を使用します
- パフォーマンスの問題には performance-audit を使用します
- セキュリティの脆弱性には security-audit を使用します
リソース
references/debugging-patterns.md- カテゴリー別の一般的なエラーパターンscripts/validate-debugging.sh- デバッグの実践を自動検証するスクリプト- Chrome DevTools - https://developer.chrome.com/docs/devtools/
- React DevTools - https://react.dev/learn/react-developer-tools
- Node.js デバッグガイド - https://nodejs.org/en/docs/guides/debugging-getting-started/