performance-optimization
Optimizes application performance across frontend, backend, queries, and databases. Use when performance requirements exist, when you suspect performance regressions, when Core Web Vitals or load times need improvement, when N+1 query patterns need fixing, or when profiling reveals bottlenecks.
Install
npx skills add https://github.com/addyosmani/agent-skills --skill performance-optimizationOptimization Patterns
Worked fixes for the anti-patterns named in Step 3 of the performance-optimization skill. The skill states the rule and the signature to look for; this file shows one concrete implementation. Open the section you need after Step 2 has identified the bottleneck — it is not meant to be read top to bottom.
Checklists, measurement commands, TTFB diagnosis, and caching read/write strategies live in ../../../references/performance-checklist.md.
N+1 Queries (Backend)
Unbounded Data Fetching
Queries That Ignore Their Index
"Add an index" is the guess. The query plan is the measurement:
Three things in the output decide the fix:
Index for the shape of the query, not the column in isolation. In a composite index, equality columns come first, then the range or sort column:
When an index will not help:
Re-run EXPLAIN ANALYZE after. An index that did not change the plan is a revert (Step 4), and it is not free: it still costs on every write.
Connection Pool Exhaustion
The signature is distinctive: every endpoint slows at once, the slow time is spent waiting for a connection rather than executing, and the database reports mostly idle sessions.
Bigger is not faster. A pool larger than what the database can execute concurrently just relocates the queue from your app to the database, where it is harder to see. When instance count is unbounded (serverless, autoscaling), a proxy that multiplexes connections (pgbouncer, RDS Proxy) is the fix, not a higher max.
Missing Image Optimization (Frontend)
Unnecessary Re-renders (React)
Large Bundle Size
Missing Caching (Backend)
Cache what is expensive to produce and read far more often than it changes. Caching a query that was already fast adds a network hop, a staleness bug, and an eviction policy to maintain, in exchange for nothing.
Pick the layer deliberately:
Key design decides correctness. Every input that changes the response belongs in the key: tenant, locale, permissions, feature flags. A key that omits the viewer is how one user's data gets served to another, and that ships as a performance win.
Choose one invalidation strategy, not three:
Guard against the stampede. A hot key expires, every concurrent request misses together, and the origin takes the full load at once, which is how a cache turns into an outage instead of preventing one. Serve stale while a single request recomputes (stale-while-revalidate), or coalesce concurrent misses behind one in-flight promise so N waiters cause one recompute.
Do not cache: anything whose staleness is a correctness bug (balances, permissions, inventory at checkout), or per-user data under a key that does not identify the user. See ../../../references/performance-checklist.md#caching-strategies for request coalescing, write strategies, negative caching, and the cache checklist.