systematic-debugging-laravel について
Laravel 開発者向けのデバッグスキルで、変更前に根本原因を調査する手順を定めています。調査、パターン分析、仮説の検証、修正の確認という4段階で進めます。
主な機能
- 01根本原因を追う4段階の手順
- 02Laravel ログ、Telescope、Tinker の活用
- 03パターン比較と仮説検証
- 04テストによる修正の確認
活用シーン
- 01テスト失敗やルートエラーの調査
- 02Eloquent クエリやリレーションの追跡
- 03キュー、バリデーション、性能問題の診断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iserter/ laravel-claude-agents --skill systematic-debugging-laravelインストール後、Claude Code で/systematic-debugging-laravelを入力して読み込まれたか確認します。
詳細
Laravel の体系的なデバッグ
概要
Laravel アプリケーションでの場当たり的な修正は、時間を浪費し、新たなバグを生みます。応急処置では根本的な問題を覆い隠してしまいます。
基本原則: 修正を試みる前に、必ず根本原因を見つけてください。症状への対処は失敗です。
このプロセスの文言だけでなく精神に反することは、デバッグの精神に反することです。
鉄則
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
フェーズ 1 を完了していない場合、修正を提案することはできません。
使用するタイミング
Laravel の技術的な問題には、どのようなものでも使用してください。
- テストの失敗
- Eloquent クエリの問題
- 認証/認可のバグ
- バリデーションの失敗
- キュージョブの失敗
- ルートのエラー
- マイグレーションの問題
- N+1 クエリの問題
- パフォーマンスの問題
特に次のような場合に使用してください。
- 時間に追われている
- 「すぐにできる簡単な修正」が明らかに思える
- すでに複数の修正を試した
- 前回の修正が機能しなかった
- 問題を十分に理解していない
4 つのフェーズ
次のフェーズに進む前に、各フェーズを必ず完了してください。
フェーズ 1:根本原因の調査
修正を試みる前に、必ず以下を行ってください。
-
エラーメッセージを注意深く読む
SQLSTATE[23000]: Integrity constraint violation → Check foreign key constraints, not a code bug Class 'App\Models\Post' not found → Check namespace, run composer dump-autoload Method Illuminate\Database\Eloquent\Collection::save does not exist → get() returns Collection, not Model. Use first() or update() -
Laravel のログを確認する
# Main Laravel log tail -f storage/logs/laravel.log # Check for specific errors grep "SQLSTATE" storage/logs/laravel.log # Clear logs if too large > storage/logs/laravel.log -
デバッグモードを有効にする(ローカル環境のみ)
APP_DEBUG=true APP_ENV=local -
Laravel Telescope を使用する
composer require laravel/telescope --dev php artisan telescope:install php artisan migrate # Access at /telescope # View: Requests, Queries, Jobs, Events, Exceptions -
最近の変更を確認する
# What changed that could cause this? git log --oneline -10 git diff HEAD~5 # Check if migrations ran php artisan migrate:status # Check if config cached php artisan config:show -
一貫して再現できることを確認する
# Can you trigger it every time? php artisan tinker >>> App\Models\Post::first(); # Try in different environments APP_ENV=testing php artisan test -
Eloquent の問題ではデータフローを追跡する
// Enable query logging DB::listen(function ($query) { Log::debug('Query executed', [ 'sql' => $query->sql, 'bindings' => $query->bindings, 'time' => $query->time, ]); }); // Or in specific code DB::enableQueryLog(); $posts = Post::with('user')->get(); dd(DB::getQueryLog());
フェーズ 2:パターンの分析
修正する前にパターンを見つけてください。
-
Laravel にある正常に動作する例を見つける
# Search for similar working code grep -r "belongsTo" app/Models/ grep -r "middleware" app/Http/ # Check Laravel docs for the pattern # Check other models that work correctly -
Laravel の規約と照らし合わせる
// ❌ What you have class Post extends Model { public function author() { return $this->hasOne(User::class, 'id', 'user_id'); } } // ✅ Laravel convention class Post extends Model { public function user(): BelongsTo { return $this->belongsTo(User::class); } } -
Laravel のドキュメントを確認する
- 該当するセクションを斜め読みせず、最後まで読む
- まずは例に正確に従う
- 理解した後でのみカスタマイズする
-
違いを特定する
// Working model class User extends Model { protected $fillable = ['name', 'email']; } // Broken model - difference: missing mass assignment protection class Post extends Model { // No $fillable or $guarded defined }
フェーズ 3:仮説とテスト
科学的手法に従ってください。
-
仮説を 1 つ立てる
Hypothesis: "Posts aren't saving because mass assignment protection is blocking the 'user_id' field" Expected: Adding 'user_id' to $fillable will fix it -
最小限のテストを行う
// Before (broken) protected $fillable = ['title', 'content']; // Test change (ONE variable) protected $fillable = ['title', 'content', 'user_id']; // Don't change multiple things at once -
Tinker で検証する
php artisan tinker >>> $post = Post::create(['title' => 'Test', 'content' => 'Test', 'user_id' => 1]); >>> $post->user_id; // Should be 1 -
分からない場合
- 「なぜ X が起きているのか分かりません」と伝える
- 同様の問題について Laravel の GitHub の課題を確認する
- 詳細を添えて Laravel の Discord/フォーラムで質問する
- 分かったふりをしない
フェーズ 4:実装
症状ではなく、根本原因を修正してください。
-
失敗するテストケースを作成する
use Illuminate\Foundation\Testing\RefreshDatabase; test('user can create post', function () { $user = User::factory()->create(); $response = $this->actingAs($user) ->post('/posts', [ 'title' => 'Test Post', 'content' => 'Test content', ]); $response->assertRedirect(); expect(Post::where('title', 'Test Post')->exists())->toBeTrue(); expect(Post::first()->user_id)->toBe($user->id); }); // Run and watch it FAIL first php artisan test --filter=user_can_create_post -
修正を 1 つ実装する
// ONE fix for the root cause protected $fillable = ['title', 'content', 'user_id']; // NO bundled improvements like: // - Adding casts // - Refactoring methods // - Changing other code -
修正を検証する
# Test passes now php artisan test --filter=user_can_create_post # All tests still pass php artisan test # Manual verification php artisan tinker >>> $post = Post::create([...]); -
修正が機能しない場合
- 停止する
- これまでに何回修正を試したか数える
- 3 回未満の場合:新しい情報をもとにフェーズ 1 に戻る
- 3 回以上の場合:停止して、アプローチを見直す
-
3 回以上の修正に失敗した場合:アーキテクチャを見直す
Pattern indicating architectural problem: - Each fix reveals new shared state/coupling - Fixes require "massive refactoring" - Each fix creates new symptoms elsewhere STOP and question fundamentals: - Is this Laravel pattern correct? - Should we use a different approach (repository/service)? - Are we fighting the framework? Discuss with team before attempting more fixes.
Laravel 固有のデバッグ手法
Eloquent のデバッグ
// See actual SQL
$posts = Post::where('status', 'published');
dd($posts->toSql(), $posts->getBindings());
// Check relationship loading
$post = Post::first();
$post->relationLoaded('user'); // false
$post->load('user');
$post->relationLoaded('user'); // true
// Prevent lazy loading (catch N+1)
Model::preventLazyLoading(!app()->isProduction());
ルートのデバッグ
# List all routes
php artisan route:list
# Find specific route
php artisan route:list --name=posts
# Check route exists
php artisan tinker
>>> route('posts.show', 1);
キューのデバッグ
# See failed jobs
php artisan queue:failed
# Retry failed job
php artisan queue:retry <id>
# Work queue with verbose output
php artisan queue:work --verbose
# Check job payload
php artisan tinker
>>> DB::table('jobs')->first();
バリデーションのデバッグ
// See exact validation errors
protected function failedValidation(Validator $validator)
{
Log::debug('Validation failed', [
'errors' => $validator->errors()->toArray(),
'input' => $this->all(),
]);
parent::failedValidation($validator);
}
危険信号 — 停止して手順に従ってください
次のように考えていることに気づいたら:
- 「とりあえず応急処置をして、後で調査しよう」
- 「X を変更して、動くか試してみよう」
- 「効果があるか確認するために
protected $guarded = []を追加しよう」 - 「テストを省略して、手動で確認しよう」
- 「おそらくリレーションの定義が原因だろう」
- 「Eloquent を十分に理解していないけれど、これで動くかもしれない」
- 「ドキュメントには X と書いてあるけれど、違う方法で応用しよう」
- 「もう 1 回だけ修正を試そう」(すでに 2 回以上試している場合)
これらはすべて、停止してフェーズ 1 に戻るべきサインです。
Laravel のよくあるデバッグシナリオ
シナリオ 1:N+1 クエリの問題
Phase 1: Detect it
- Enable Model::preventLazyLoading()
- Exception thrown showing the problem
Phase 2: Find the pattern
- Check working code that uses with()
- Identify which relationship is lazy loading
Phase 3: Hypothesis
- "Adding with('user') will prevent the N+1"
Phase 4: Fix
- Add test that counts queries
- Add with('user') to the query
- Verify query count reduced
シナリオ 2:ルートモデルバインディングが機能しない
Phase 1: Investigate
- Check route definition: /posts/{post}
- Check controller parameter: Post $post
- Check if using custom key
Phase 2: Pattern
- Compare with working route binding
- Check Post model for getRouteKeyName()
Phase 3: Hypothesis
- "Parameter name doesn't match or model not found"
Phase 4: Fix
- Ensure route parameter matches method parameter
- Or customize: public function getRouteKeyName() { return 'slug'; }
シナリオ 3:一括代入の例外
Phase 1: Error says "Add [field] to fillable property"
Phase 2: Check other models' $fillable arrays
Phase 3: Hypothesis: "Field not in $fillable"
Phase 4: Add field to $fillable, test
Laravel エージェントとの連携
- Laravel 固有のデバッグ支援には laravel-debugger を使用する
- 失敗するテストの作成(フェーズ 4)には laravel-testing-expert を使用する
- リレーションのデバッグには eloquent-specialist を使用する
- パフォーマンスの問題には laravel-performance-optimizer を使用する
クイックリファレンス
| フェーズ | Laravel 固有の作業 | 成功基準 |
|---|---|---|
| 1. 根本原因 | ログ、Telescope、Tinker、最近の変更を確認する | 問題の内容と原因を理解する |
| 2. パターン | 動作する Laravel の例を探し、ドキュメントを確認する | 違いを特定する |
| 3. 仮説 | 仮説を立て、Tinker でテストする | 仮説を確認するか、新しい仮説を立てる |
| 4. 実装 | Pest テストを作成し、修正して検証する | バグが解決し、テストが成功する |
忘れないでください
- Laravel のエラーメッセージは非常に優れているため、最後まで読みましょう
- 包括的なデバッグには Telescope を使いましょう
- 仮説の検証には Tinker が役立ちます
- Laravel の規約に従いましょう。フレームワークに逆らうとバグの原因になります
- 「Laravel の奇妙な動作」の 95% は、フレームワークへの理解不足が原因です
常に体系的に調査して根本原因を把握し、正しい方法で一度で修正しましょう。