Use when a page, screen or endpoint feels slow, when a ticket touches loading, rendering, queries or lists, before any release of a web feature, and whenever an agent is about to say something is "fast" or "faster". Measure first, targets as rows, re-measure the same way, keep or revert, guard in CI.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Performance?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/fadi88-performance-one-man-startup)
---
name: performance
description: Use when a page, screen or endpoint feels slow, when a ticket touches loading, rendering, queries or lists, before any release of a web feature, and whenever an agent is about to say something is "fast" or "faster". Measure first, targets as rows, re-measure the same way, keep or revert, guard in CI.
---
# Performance
**Measure before optimising.** Performance work without a measurement is a guess, and a guess
that looks like a win is the most expensive kind.
## Targets are the founder's rows
The numbers below are defaults to offer, not ours to set (rule 2). Put them to the founder at
setup or in the feature's grilling, and write the chosen ones as requirement rows the tests
assert. Source for the defaults: Google's Core Web Vitals thresholds as collected in
addyosmani/agent-skills, read 2026-10-21.
| Measure | Default to offer |
|---|---|
| Largest Contentful Paint (LCP) | 2.5 s or less |
| Interaction to Next Paint (INP) | 200 ms or less |
| Cumulative Layout Shift (CLS) | 0.1 or less |
| Main-thread task | none over 50 ms |
| Initial JavaScript, gzipped | under 200 KB |
| API response, p95 | under 200 ms |
| Lighthouse performance score | 90 or more |
Measure on the slowest connection and device the audience is likely to have
(`project.toml [product] audience`), not on the developer's machine.
## The loop
1. **Baseline.** Measure with a named tool and a fixed command (Lighthouse, the browser's
performance panel, `EXPLAIN ANALYZE`, a load script). Run it three times; the spread is
your noise floor. Write the command and the numbers in `docs/work/<id>/perf.md`.
2. **Find the bottleneck** in the measurement, not in a hunch. The slow part is rarely the
part that looks slow.
3. **Change one thing.**
4. **Re-measure the same way.** Same command, same conditions, same number of runs.
5. **Keep or revert.** Keep only when the gain is larger than the noise floor and every test
still passes. Within noise is a revert; "neutral" is a revert. Log the attempt either way.
6. **Guard.** A target that was reached gets a check: a budget in CI, a Lighthouse run, a
field measurement the dashboard shows. A win nobody guards is lost by next month.
## The scorecard
Before a web feature is released, `qa` writes a scorecard in `qa.md`:
| Metric | Value | Source | Target (row) | Status |
|---|---|---|---|---|
Source is one of: field (real users), lab (Lighthouse or a local run), trace (a profile), or
"not measured". A value with no source is not written. Lab is never presented as field.
## What to look for
**Server and data**
- A query inside a loop (N+1): fetch the relation in one query.
- A list endpoint without a limit and a stable order: paginate every list.
- An index added without a before-and-after query plan: run `EXPLAIN ANALYZE` first.
- A connection pool raised instead of finding what holds the connections.
- A cache on something that must be fresh (balances, permissions, stock); a cache key that
misses one input that changes the answer (tenant, locale, role, flag).
**Browser**
- Images without `width` and `height` (layout shift), the LCP image without
`fetchpriority="high"`, images above the fold lazily loaded or below it eagerly.
- Everything in one bundle: split by route, load rare features on demand.
- Memoisation everywhere "to be safe": memoise only where the profile shows the cost.
- Work on the main thread during input: break it up or move it off.
## When you catch yourself saying
| The excuse | The answer |
|---|---|
| "This will obviously be faster." | Measure it. Obvious wins are often within noise, and some are losses. |
| "I'll optimise it while I'm in here." | A change the ticket did not ask for, unmeasured, is a risk with no upside recorded. |
| "The number went down." | By more than the spread between runs? Same command, same conditions? |
| "Users have fast phones." | The row names the device and connection. Measure on those. |
| "I'll add the budget later." | Later is after the regression. The guard ships with the win. |
Red flags: an index without a query plan; a pool size raised; a list without a page size; a
win that needed a test changed or deleted; a scorecard value with no source.