Skill — Performance Engineering
When this skill activates
Any task involving response time, resource usage, bundle size, database query
performance, or user-perceived load time metrics.
Mandatory actions when this skill is active
Before writing any code
- Identify what is being measured. Never optimise without a baseline.
- Read the relevant metric from REQUIREMENTS.md (NFRs):
- API response time target (e.g., p95 < 200ms)
- Page load time target (e.g., LCP < 2.5s)
- Bundle size budget (e.g., < 200KB gzipped initial JS)
- If no NFR is defined: ask the user to define one before optimising.
"Optimisation without a target is premature optimisation."
Backend performance standards
Database queries:
- Every query must use indexes for its WHERE, JOIN, and ORDER BY columns
- Detect N+1 queries: if fetching a list then querying per item, use JOIN or batch fetch
- Pagination: always paginate list endpoints (default page size: 20, max: 100)
- Avoid
SELECT * — select only the columns needed
- Use
EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN (MySQL) to verify query plans
- Cache repeated identical queries: Redis with appropriate TTL
API response time:
- Default targets (override with NFRs): p50 < 100ms, p95 < 500ms, p99 < 2000ms for most endpoints
- Slow endpoints (> 500ms): must be async (return immediately, use webhooks or polling)
- Database connection pooling: always use a connection pool (never open/close per request)
- Pool sizing: start with
min=2, max=CPU * 2 + 2 per instance, then tune to DB limits and workload
- Serverless: prefer a DB proxy (PgBouncer, RDS Proxy) or driver-level pooling that supports bursty concurrency
- Avoid synchronous I/O in request handlers
- Cache hot DB reads at the query or service layer when data is read-heavy and tolerant of staleness
Caching strategy:
Defaults below — tune per data freshness requirements and invalidate on writes.
| Data type |
Recommended cache |
TTL |
| User session data |
Redis |
24 hours |
| Computed aggregates |
Redis |
1–5 minutes |
| Static reference data |
Redis |
1 hour |
| User-specific data |
Redis with user key |
15 minutes |
| API responses |
HTTP Cache-Control |
depends on freshness needs |
Frontend performance standards
Bundle size budgets:
| Asset |
Budget (gzipped) |
| Initial JavaScript |
< 200KB |
| Initial CSS |
< 50KB |
| Per-route chunk |
< 100KB |
| Images (hero) |
< 200KB WebP |
| Fonts |
< 50KB per weight |
Core Web Vitals targets (Google's thresholds):
| Metric |
Good |
Needs improvement |
Poor |
| LCP (Largest Contentful Paint) |
< 2.5s |
2.5–4s |
> 4s |
| INP (Interaction to Next Paint) |
< 200ms |
200–500ms |
> 500ms |
| CLS (Cumulative Layout Shift) |
< 0.1 |
0.1–0.25 |
> 0.25 |
Implementation patterns:
- Route-based code splitting: every route is its own chunk
- Lazy load non-critical components:
React.lazy() + Suspense
- Image optimisation: use
next/image or equivalent. Always specify width/height.
- Font loading:
font-display: swap. Preload critical fonts.
- Avoid layout thrashing: batch DOM reads before DOM writes
- Debounce user input handlers (search: 300ms, resize: 100ms)
- Memoize expensive computations:
useMemo / useCallback where measured
SSR/SSG guidance:
- Prefer SSG for marketing and content pages with low data volatility
- Prefer SSR for personalized data, but watch TTFB and cache at the edge where possible
- For hybrid apps, stream server components or HTML where supported to reduce TTFB and improve LCP
Performance measurement commands
# Backend: measure API response time
curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/endpoint
# Frontend: Lighthouse CI
npx lighthouse https://example.com --output json --output-path ./lighthouse.json
# Bundle analysis
npx bundle-analyzer stats.json
# Node.js profiling
node --prof app.js
node --prof-process isolate-*.log > profile.txt
# Database: explain query
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
Performance review checklist
Before marking any task done that involves a query or endpoint:
Output
Write performance notes to SUMMARY.md:
- Baseline metric (before)
- Achieved metric (after)
- What optimisation was applied
- Whether the NFR target was met ✅ or still needs work ⚠️
1---2name: performance3description: Skill — Performance Engineering4---56# Skill — Performance Engineering78## When this skill activates9Any task involving response time, resource usage, bundle size, database query10performance, or user-perceived load time metrics.1112## Mandatory actions when this skill is active1314### Before writing any code151. Identify what is being measured. Never optimise without a baseline.162. Read the relevant metric from REQUIREMENTS.md (NFRs):17 - API response time target (e.g., p95 < 200ms)18 - Page load time target (e.g., LCP < 2.5s)19 - Bundle size budget (e.g., < 200KB gzipped initial JS)203. If no NFR is defined: ask the user to define one before optimising.21 "Optimisation without a target is premature optimisation."2223### Backend performance standards2425**Database queries:**26- Every query must use indexes for its WHERE, JOIN, and ORDER BY columns27- Detect N+1 queries: if fetching a list then querying per item, use JOIN or batch fetch28- Pagination: always paginate list endpoints (default page size: 20, max: 100)29- Avoid `SELECT *` — select only the columns needed30- Use `EXPLAIN ANALYZE` (PostgreSQL) or `EXPLAIN` (MySQL) to verify query plans31- Cache repeated identical queries: Redis with appropriate TTL3233**API response time:**34- Default targets (override with NFRs): p50 < 100ms, p95 < 500ms, p99 < 2000ms for most endpoints35- Slow endpoints (> 500ms): must be async (return immediately, use webhooks or polling)36- Database connection pooling: always use a connection pool (never open/close per request)37- Pool sizing: start with `min=2`, `max=CPU * 2 + 2` per instance, then tune to DB limits and workload38- Serverless: prefer a DB proxy (PgBouncer, RDS Proxy) or driver-level pooling that supports bursty concurrency39- Avoid synchronous I/O in request handlers40- Cache hot DB reads at the query or service layer when data is read-heavy and tolerant of staleness4142**Caching strategy:**43Defaults below — tune per data freshness requirements and invalidate on writes.44| Data type | Recommended cache | TTL |45|---|---|---|46| User session data | Redis | 24 hours |47| Computed aggregates | Redis | 1–5 minutes |48| Static reference data | Redis | 1 hour |49| User-specific data | Redis with user key | 15 minutes |50| API responses | HTTP Cache-Control | depends on freshness needs |5152### Frontend performance standards5354**Bundle size budgets:**55| Asset | Budget (gzipped) |56|---|---|57| Initial JavaScript | < 200KB |58| Initial CSS | < 50KB |59| Per-route chunk | < 100KB |60| Images (hero) | < 200KB WebP |61| Fonts | < 50KB per weight |6263**Core Web Vitals targets (Google's thresholds):**64| Metric | Good | Needs improvement | Poor |65|---|---|---|---|66| LCP (Largest Contentful Paint) | < 2.5s | 2.5–4s | > 4s |67| INP (Interaction to Next Paint) | < 200ms | 200–500ms | > 500ms |68| CLS (Cumulative Layout Shift) | < 0.1 | 0.1–0.25 | > 0.25 |6970**Implementation patterns:**71- Route-based code splitting: every route is its own chunk72- Lazy load non-critical components: `React.lazy()` + `Suspense`73- Image optimisation: use `next/image` or equivalent. Always specify `width`/`height`.74- Font loading: `font-display: swap`. Preload critical fonts.75- Avoid layout thrashing: batch DOM reads before DOM writes76- Debounce user input handlers (search: 300ms, resize: 100ms)77- Memoize expensive computations: `useMemo` / `useCallback` where measured7879**SSR/SSG guidance:**80- Prefer SSG for marketing and content pages with low data volatility81- Prefer SSR for personalized data, but watch TTFB and cache at the edge where possible82- For hybrid apps, stream server components or HTML where supported to reduce TTFB and improve LCP8384### Performance measurement commands8586```bash87# Backend: measure API response time88curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/endpoint8990# Frontend: Lighthouse CI91npx lighthouse https://example.com --output json --output-path ./lighthouse.json9293# Bundle analysis94npx bundle-analyzer stats.json9596# Node.js profiling97node --prof app.js98node --prof-process isolate-*.log > profile.txt99100# Database: explain query101EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';102```103104## Performance review checklist105Before marking any task done that involves a query or endpoint:106- [ ] Query uses appropriate indexes (verified with EXPLAIN)107- [ ] No N+1 queries in list endpoints108- [ ] Response time verified locally (curl with timing)109- [ ] No `SELECT *` in production queries110- [ ] Caching applied where data is read-heavy and tolerance allows staleness111112## Output113Write performance notes to SUMMARY.md:114- Baseline metric (before)115- Achieved metric (after)116- What optimisation was applied117- Whether the NFR target was met ✅ or still needs work ⚠️