Benchmark (Laravel)
Measure real performance baselines for a Laravel app and detect regressions by
comparing before/after a change. This skill measures — it produces honest
numbers and verdicts. For design guidance on hot paths, defer to
@skills/latency-critical-systems/SKILL.md; to act on a regression with an
optimization loop, hand off to @skills/benchmark-optimization-loop/SKILL.md.
Constraints
- Apply
@rules/sql/optimalize.mdc when reading or interpreting query timing (N+1, index usage, SARGable filters).
- Apply
@rules/code-testing/general.mdc if you add or touch any benchmark test (Pest, no describe()).
- Measure, never guess — every number must be a real readback from a running system, command, or tool.
- Control for noise: separate warm vs cold, run repeats (≥5), report median plus p95, fix one variable at a time.
- Same machine, same dataset, same config for before and after — note the environment so a teammate can reproduce.
- Keep secrets and private payloads out of baseline JSON and logs.
Use when
- You need a performance baseline for a page, API route, build step, or query.
- A change (PR, dependency bump, query rewrite, config change) may have caused a regression and you must prove it either way.
- You are comparing two implementations or stack options on the same workload.
- Someone claims "it's faster/slower" without a measured before/after.
Do not use for live design advice (use latency-critical-systems) or for
diagnosing one Telescope request (use @skills/laravel-telescope/SKILL.md).
Execution
Pick the modes that match the change. Always record environment first:
app URL, commit SHA, APP_ENV, dataset size, cache/opcache state, and whether
the run is cold (first hit, caches cleared) or warm (steady state).
Mode A — Page performance (Core Web Vitals)
Measure against the running Laravel app (php artisan serve or the real host).
- Run Lighthouse:
npx lighthouse <url> --output=json --output-path=./lh.json --only-categories=performance --chrome-flags="--headless".
- Capture: LCP (target < 2.5s), CLS (< 0.1), INP (< 200ms), FCP (< 1.8s), TTFB (< 0.8s).
- Capture resource weight: total transfer, JS/CSS bytes (post-Vite build), render-blocking resources, request count.
- Run 3–5 times; report median. Lighthouse is noisy — discard the first (cold) run unless cold is the metric you want.
Mode B — API performance (latency percentiles + load)
Target real Laravel routes/controllers. Authenticate as needed; use a realistic payload.
- Quick percentiles:
wrk -t4 -c50 -d30s --latency https://app.test/api/orders (reports p50/p90/p99).
- Apache Bench alternative:
ab -n 1000 -c 50 https://app.test/api/orders.
- Scripted scenarios (auth, POST bodies, ramp): use
k6 (k6 run script.js).
- Capture: p50 / p95 / p99 latency, throughput (req/s), error rate, response size. Warm the route first, then measure.
Mode C — Build / dev velocity
composer install (cold = no vendor, no cache): time it.
- Asset build:
time npm run build (Vite production) and HMR warm-reload feel.
- Test suite:
time php artisan test or vendor/bin/pest (note parallel vs serial).
- Static analysis:
time vendor/bin/phpstan analyse and time vendor/bin/pint --test.
- Run each at least twice; report cold and warm separately (Composer/PHPStan caches matter).
Mode D — DB query timing
- Identify the hot query via
@skills/laravel-telescope/SKILL.md or the slow-query log.
- Run
EXPLAIN (or EXPLAIN ANALYZE on MySQL 8) per @rules/sql/optimalize.mdc; record rows examined, key used, and whether a full scan occurs.
- Time the query itself, not the surrounding PHP, to isolate DB cost; cross-check with the Telescope duration for the same request.
Before / After comparison
- On the base state (before the change), run the relevant modes and write the baseline JSON.
- Apply the change. Re-run the same modes on the same environment and dataset.
- Compute the delta and verdict per metric. Re-run any metric whose delta is within run-to-run noise before declaring a regression or win.
Output
Baseline JSON
Store git-tracked, one file per logical target, under tests/Benchmark/ (or
.benchmarks/ if you prefer a non-test location). Commit so the team shares one
reference.
{
"target": "GET /api/orders",
"mode": "api",
"captured_at": "2026-06-15T10:00:00Z",
"commit": "abc1234",
"environment": { "app_env": "local", "dataset": "10k orders", "state": "warm", "runs": 5, "tool": "wrk" },
"metrics": {
"p50_ms": 42,
"p95_ms": 110,
"p99_ms": 180,
"throughput_rps": 940,
"error_rate": 0.0
}
}
For page mode use keys lcp_ms, cls, inp_ms, fcp_ms, ttfb_ms,
js_bytes, transfer_bytes; for build mode seconds per step (cold/warm);
for DB mode query_ms, rows_examined, key_used.
Comparison table
Report one row per metric with the delta and an explicit verdict.
| Metric |
Before |
After |
Delta |
Verdict |
| p95 (ms) |
110 |
78 |
-29% |
improved |
| p99 (ms) |
180 |
175 |
-3% |
noise |
| throughput (rps) |
940 |
1280 |
+36% |
improved |
| error rate |
0.0% |
0.4% |
+0.4pp |
regressed |
Verdict legend: improved / regressed / noise (delta within run-to-run
variance) / target-miss (still below the target threshold). State the
threshold and the noise band you used.
Done when
- Environment is recorded (URL, commit, env, dataset, cold/warm, run count, tool) so the run is reproducible.
- Each in-scope metric has a real measured value from at least the agreed number of repeat runs — never an estimate.
- A git-tracked baseline JSON exists for every measured target.
- For a change review, a before/after comparison table with per-metric delta and verdict is produced, and noise-band deltas are not reported as wins or regressions.
- Any confirmed regression or target-miss is flagged for
@skills/benchmark-optimization-loop/SKILL.md or @skills/mysql-problem-solver/SKILL.md as appropriate.
1---2name: benchmark3description: Use when measuring performance baselines or detecting regressions before and after a change in a Laravel app — page Core Web Vitals, API latency percentiles, build/test velocity, and DB query timing, stored as git-tracked baselines for team comparison.4license: MIT5---67# Benchmark (Laravel)89Measure real performance baselines for a Laravel app and detect regressions by10comparing before/after a change. This skill **measures** — it produces honest11numbers and verdicts. For design guidance on hot paths, defer to12`@skills/latency-critical-systems/SKILL.md`; to act on a regression with an13optimization loop, hand off to `@skills/benchmark-optimization-loop/SKILL.md`.1415## Constraints16- Apply `@rules/sql/optimalize.mdc` when reading or interpreting query timing (N+1, index usage, SARGable filters).17- Apply `@rules/code-testing/general.mdc` if you add or touch any benchmark test (Pest, no `describe()`).18- Measure, never guess — every number must be a real readback from a running system, command, or tool.19- Control for noise: separate warm vs cold, run repeats (≥5), report median plus p95, fix one variable at a time.20- Same machine, same dataset, same config for before and after — note the environment so a teammate can reproduce.21- Keep secrets and private payloads out of baseline JSON and logs.2223## Use when24- You need a performance baseline for a page, API route, build step, or query.25- A change (PR, dependency bump, query rewrite, config change) may have caused a regression and you must prove it either way.26- You are comparing two implementations or stack options on the same workload.27- Someone claims "it's faster/slower" without a measured before/after.2829Do not use for live design advice (use `latency-critical-systems`) or for30diagnosing one Telescope request (use `@skills/laravel-telescope/SKILL.md`).3132## Execution3334Pick the modes that match the change. Always record environment first:35app URL, commit SHA, `APP_ENV`, dataset size, cache/opcache state, and whether36the run is **cold** (first hit, caches cleared) or **warm** (steady state).3738### Mode A — Page performance (Core Web Vitals)39Measure against the running Laravel app (`php artisan serve` or the real host).40- Run Lighthouse: `npx lighthouse <url> --output=json --output-path=./lh.json --only-categories=performance --chrome-flags="--headless"`.41- Capture: **LCP** (target < 2.5s), **CLS** (< 0.1), **INP** (< 200ms), **FCP** (< 1.8s), **TTFB** (< 0.8s).42- Capture resource weight: total transfer, JS/CSS bytes (post-Vite build), render-blocking resources, request count.43- Run 3–5 times; report median. Lighthouse is noisy — discard the first (cold) run unless cold is the metric you want.4445### Mode B — API performance (latency percentiles + load)46Target real Laravel routes/controllers. Authenticate as needed; use a realistic payload.47- Quick percentiles: `wrk -t4 -c50 -d30s --latency https://app.test/api/orders` (reports p50/p90/p99).48- Apache Bench alternative: `ab -n 1000 -c 50 https://app.test/api/orders`.49- Scripted scenarios (auth, POST bodies, ramp): use `k6` (`k6 run script.js`).50- Capture: **p50 / p95 / p99** latency, throughput (req/s), error rate, response size. Warm the route first, then measure.5152### Mode C — Build / dev velocity53- `composer install` (cold = no vendor, no cache): time it.54- Asset build: `time npm run build` (Vite production) and HMR warm-reload feel.55- Test suite: `time php artisan test` or `vendor/bin/pest` (note parallel vs serial).56- Static analysis: `time vendor/bin/phpstan analyse` and `time vendor/bin/pint --test`.57- Run each at least twice; report cold and warm separately (Composer/PHPStan caches matter).5859### Mode D — DB query timing60- Identify the hot query via `@skills/laravel-telescope/SKILL.md` or the slow-query log.61- Run `EXPLAIN` (or `EXPLAIN ANALYZE` on MySQL 8) per `@rules/sql/optimalize.mdc`; record rows examined, key used, and whether a full scan occurs.62- Time the query itself, not the surrounding PHP, to isolate DB cost; cross-check with the Telescope duration for the same request.6364### Before / After comparison651. On the base state (before the change), run the relevant modes and write the baseline JSON.662. Apply the change. Re-run the **same** modes on the **same** environment and dataset.673. Compute the delta and verdict per metric. Re-run any metric whose delta is within run-to-run noise before declaring a regression or win.6869## Output7071### Baseline JSON72Store git-tracked, one file per logical target, under `tests/Benchmark/` (or73`.benchmarks/` if you prefer a non-test location). Commit so the team shares one74reference.7576```json77{78 "target": "GET /api/orders",79 "mode": "api",80 "captured_at": "2026-06-15T10:00:00Z",81 "commit": "abc1234",82 "environment": { "app_env": "local", "dataset": "10k orders", "state": "warm", "runs": 5, "tool": "wrk" },83 "metrics": {84 "p50_ms": 42,85 "p95_ms": 110,86 "p99_ms": 180,87 "throughput_rps": 940,88 "error_rate": 0.089 }90}91```9293For page mode use keys `lcp_ms`, `cls`, `inp_ms`, `fcp_ms`, `ttfb_ms`,94`js_bytes`, `transfer_bytes`; for build mode `seconds` per step (cold/warm);95for DB mode `query_ms`, `rows_examined`, `key_used`.9697### Comparison table98Report one row per metric with the delta and an explicit verdict.99100| Metric | Before | After | Delta | Verdict |101| --- | --- | --- | --- | --- |102| p95 (ms) | 110 | 78 | -29% | improved |103| p99 (ms) | 180 | 175 | -3% | noise |104| throughput (rps) | 940 | 1280 | +36% | improved |105| error rate | 0.0% | 0.4% | +0.4pp | regressed |106107Verdict legend: `improved` / `regressed` / `noise` (delta within run-to-run108variance) / `target-miss` (still below the target threshold). State the109threshold and the noise band you used.110111## Done when112- Environment is recorded (URL, commit, env, dataset, cold/warm, run count, tool) so the run is reproducible.113- Each in-scope metric has a real measured value from at least the agreed number of repeat runs — never an estimate.114- A git-tracked baseline JSON exists for every measured target.115- For a change review, a before/after comparison table with per-metric delta and verdict is produced, and noise-band deltas are not reported as wins or regressions.116- Any confirmed regression or target-miss is flagged for `@skills/benchmark-optimization-loop/SKILL.md` or `@skills/mysql-problem-solver/SKILL.md` as appropriate.