The debugging workflow
Identify the symptom precisely
Which route? Which user segment? Which time window? 'The app is slow' is not actionable. 'POST /api/checkout p95 went from 400ms to 2.1s after 14:00 UTC on 2026-04-12' is.
Pull a trace for a slow instance
Find the slowest requests in the affected time window. Open a trace — APMs break the request into spans (DB queries, cache calls, HTTP, view rendering). The largest span is your starting point.
Classify the bottleneck layer
App code, DB, cache, external HTTP, or view rendering. ~80% of the time it's DB. Of DB issues, ~70% are N+1 queries. Narrow fast.
Reproduce locally with realistic data
Seed staging or local from anonymized production. Hit the same endpoint with similar payloads. Reproduce with Telescope or Clockwork for call-level detail Xdebug can add if needed.
Fix and deploy behind a flag
Use a feature flag so you can roll back instantly. Verify fix reduces p95 in production before removing the flag.
Document the root cause
A one-paragraph post-mortem per incident beats repeating the same discovery six months later. Include the trace URL, the fix, and the monitoring gap that let it land.
Tools by situation
| Situation | Tool | Why |
|---|---|---|
| Production trace needed | NightOwl / Nightwatch Cloud | Always-on per-request traces |
| Local call-level profiling | Xdebug + KCacheGrind | Function-level timing depth |
| Local request inspection | Telescope / Clockwork | Per-request query + event detail |
| DB query plan analysis | EXPLAIN ANALYZE | Planner decisions that matter |
| Production-safe profiling | SPX (PHP profiler) | Low-overhead, sampled |
| Memory debugging | memory_get_peak_usage in middleware | Cheap and precise |
What you should never do in production
- Run Xdebug — 10x+ overhead on every request
- Drop into tinker on a live server — one typo and you've mass-updated a table
- Add
dd()ordump()calls — they return raw HTML from every request - Enable query logging globally without sampling — will bloat your DB or log store in minutes
- Run EXPLAIN ANALYZE on write queries — it actually executes them
THE EASY WAY
Traces so you never debug in prod
NightOwl records every request with full per-span context — DB queries with durations, cache hits/misses, outgoing HTTP calls. Click a slow request and see exactly which span owned the latency. No more guessing or reproducing blind.
composer require nightowl/agent
php artisan nightowl:installFrom $5/month flat. Data in your PostgreSQL.