What this prompt does
This turns Claude Code into a performance engineer that audits your codebase end to end instead of guessing. It walks four layers in a fixed order — database, application, caching, then framework-specific — and forces concrete evidence at each step: every N+1 query reported with file:line, missing indexes inferred from real query patterns, expensive full-table scans and oversized JOINs called out by name. Because Claude Code can actually read your repo, those line citations are real, not invented.
The structure is what makes it reliable. By demanding a current-code / optimized-code / expected-improvement / risk block for every fix, it stops the model from dumping a vague "add caching" list and makes it commit to a measurable change you can review and revert. The closing instruction — prioritize by impact, fix the biggest bottleneck first, total the expected gain — keeps the output ordered like a real optimization sprint rather than a checklist.
The [current_perf] and [target_perf] variables anchor the work to a number, so recommendations are weighed against the gap you actually need to close.
When to use it
- A page or endpoint crossed an unacceptable latency threshold and you need the bottleneck named, not theorized.
- Your APM (Telescope, Clockwork, New Relic) shows query counts ballooning and you suspect N+1s but haven't traced them to source lines.
- Before a traffic spike — launch, campaign, migration — you want a ranked hit list of what breaks first under load.
- A specific Eloquent/ORM-heavy controller or report is slow and you want eager-loading and indexing recommendations grounded in the real code.
- You're deciding what to cache and for how long, and need an invalidation strategy instead of guessing TTLs.
Example output
## Optimization Plan — /dashboard (Laravel) | 1,400ms → target 300ms
### 1. [HIGH] N+1 on order items — app/Http/Controllers/DashboardController.php:42
Current: $orders->map(fn($o) => $o->items->sum('price')) // 1 + N queries (118 total)
Fixed: Order::with('items')->get() // 2 queries
Expected: ~620ms saved (118 → 2 queries) Risk: Low — read-only, covered by tests
### 2. [HIGH] Missing index — orders.user_id + created_at
Evidence: WHERE user_id = ? ORDER BY created_at → full scan, 84k rows
Fixed: composite index (user_id, created_at)
Expected: ~310ms saved Risk: Low — additive migration
### 3. [MED] Recompute on every request — DashboardController.php:71
Fixed: Cache::remember("stats:$userId", 600, ...) invalidate on Order saved
Expected: ~180ms saved Risk: Medium — needs observer wiring
Total estimated: 1,400ms → ~290ms (-79%)
Pro tips
- Be ruthless with
[symptoms]. "Slow under load at ~200 concurrent users, fine when idle" points Claude at connection-pool and locking issues; "slow on first load only" points it at cold caches. The vaguer the symptom, the more generic the audit. - Treat the estimated percentages as a priority ranking, not a benchmark. The model can't measure your hardware. Apply the top fix, re-profile, then feed the new numbers back — the prompt is designed to be re-run as you close the gap.
- Point it at one
[target_area](a controller, a job, an endpoint), not the whole app. A scoped target gets you precisefile:linefindings; "my whole app" gets you platitudes. - Pair it with your real profiler. Run Laravel Telescope, Debugbar, or
EXPLAINfirst, paste the slow-query log into[symptoms], and the analysis stops guessing which queries are hot. - Always action the Risk column before merging. "Low risk, read-only" fixes ship same day; anything touching cache invalidation or indexes on a huge table gets reviewed and deployed off-peak.