Performance Optimization
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: performance for comprehensive documentation.
When NOT to Use This Skill
This skill focuses on runtime performance optimization. Do NOT use for:
- Algorithm optimization - Use computer science/data structures fundamentals
- Code readability - Use
clean-code skill (don't sacrifice readability for micro-optimizations)
- Build time optimization - Use build tool specific skills (Vite, Webpack, etc.)
- Developer experience - Use DX-focused skills and tooling
- Security hardening - Use security-specific skills (performance != security)
Anti-Patterns
| Anti-Pattern |
Why It's Bad |
Performance Solution |
| Premature Optimization |
Waste time on non-bottlenecks |
Measure first, optimize bottlenecks only |
| **SELECT *** |
Fetches unnecessary data |
Select only needed columns |
| N+1 Queries |
Multiple DB roundtrips |
Use joins or eager loading |
| No Caching |
Repeated expensive computations |
Cache at appropriate layer (memory, Redis, CDN) |
| Blocking Operations |
Holds up main thread |
Use async/background jobs |
| Large Bundle |
Slow initial load |
Code splitting, lazy loading |
| No Image Optimization |
Huge assets over network |
Compress, modern formats (WebP, AVIF), lazy load |
| Missing Indexes |
Full table scans |
Add indexes on queried columns |
| Memory Leaks |
Unbounded growth |
Clean up listeners, close connections, clear refs |
| Synchronous I/O |
Blocks event loop |
Use async I/O operations |
Quick Troubleshooting
| Issue |
Diagnostic |
Solution |
| Slow page load |
Check Network tab |
Optimize images, enable compression, use CDN |
| Poor LCP |
Lighthouse audit |
Preload critical resources, optimize largest element |
| High INP |
Performance profiler |
Debounce handlers, use web workers, reduce JS |
| Layout shifts (CLS) |
Layout Shift Regions |
Set dimensions on images/embeds, avoid dynamic content |
| Slow API response |
APM tools, logging |
Add database indexes, cache responses, optimize queries |
| High memory usage |
Memory profiler |
Fix leaks, clear intervals/listeners, use weak refs |
| Large bundle |
Bundle analyzer |
Code split, tree shake, lazy load routes |
| Slow database query |
EXPLAIN ANALYZE |
Add indexes, rewrite query, partition table |
Frontend Performance
Core Web Vitals
| Metric |
Target |
Measurement |
| LCP (Largest Contentful Paint) |
< 2.5s |
Largest visible element |
| INP (Interaction to Next Paint) |
< 200ms |
Input responsiveness |
| CLS (Cumulative Layout Shift) |
< 0.1 |
Visual stability |
Optimization Techniques
// Code splitting
const Dashboard = lazy(() => import('./Dashboard'));
// Image optimization
<Image
src="/hero.jpg"
width={1200}
height={600}
priority // Above fold
placeholder="blur"
/>
// Memoization
const MemoizedComponent = memo(ExpensiveComponent);
const memoizedValue = useMemo(() => computeExpensive(a, b), [a, b]);
const memoizedFn = useCallback(() => handleClick(id), [id]);
// Virtual lists for long lists
<VirtualList items={items} itemHeight={50} />
Backend Performance
// N+1 prevention
const usersWithPosts = await prisma.user.findMany({
include: { posts: true } // Single query with join
});
// Caching
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await db.query();
await redis.setex(key, 3600, JSON.stringify(data));
// Connection pooling
const pool = new Pool({ max: 20 });
// Async processing
await queue.add('sendEmail', { userId });
Database Performance
-- Use EXPLAIN ANALYZE
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'x';
-- Add indexes for frequently queried columns
CREATE INDEX idx_users_email ON users(email);
-- Partial indexes
CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;
-- Avoid SELECT *
SELECT id, name, email FROM users;
-- Pagination
SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 0;
Checklist
| Area |
Check |
| Images |
Optimized, lazy loaded, proper format |
| JS Bundle |
Code split, tree shaken, minified |
| CSS |
Critical CSS inline, unused removed |
| Fonts |
Preloaded, subset, font-display |
| Caching |
CDN, browser cache, API cache |
| Database |
Indexes, query optimization |
Production Readiness
Monitoring Setup
// Web Vitals reporting
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics(metric: Metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
page: window.location.pathname,
});
// Use sendBeacon for reliability
navigator.sendBeacon('/analytics', body);
}
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
Performance Budget
// webpack.config.js or vite.config.ts
{
performance: {
maxAssetSize: 250000, // 250KB
maxEntrypointSize: 500000, // 500KB
hints: 'error',
},
}
// Lighthouse CI budget
// lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/'],
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'total-blocking-time': ['error', { maxNumericValue: 300 }],
},
},
},
};
Backend Optimization
// Response compression
import compression from 'compression';
app.use(compression({ threshold: 1024 }));
// Response caching headers
function setCacheHeaders(res: Response, maxAge: number) {
res.setHeader('Cache-Control', `public, max-age=${maxAge}, stale-while-revalidate=${maxAge * 2}`);
res.setHeader('Vary', 'Accept-Encoding');
}
// Streaming responses
async function streamLargeData(res: Response) {
const stream = db.users.findMany().cursor();
res.setHeader('Content-Type', 'application/json');
res.write('[');
let first = true;
for await (const user of stream) {
if (!first) res.write(',');
res.write(JSON.stringify(user));
first = false;
}
res.write(']');
res.end();
}
// Query optimization
const users = await prisma.user.findMany({
select: { id: true, name: true, email: true }, // Only needed fields
where: { isActive: true },
take: 20,
orderBy: { createdAt: 'desc' },
});
Database Optimization
-- Composite indexes for common queries
CREATE INDEX idx_users_active_created
ON users(is_active, created_at DESC)
WHERE is_active = true;
-- Query analysis
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM users WHERE email = 'test@example.com';
-- Connection pooling configuration
-- pgbouncer.ini
[pgbouncer]
pool_mode = transaction
default_pool_size = 20
max_client_conn = 100
CI Performance Testing
# .github/workflows/performance.yml
name: Performance
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v10
with:
configPath: ./lighthouserc.js
uploadArtifacts: true
bundle-size:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
- name: Check bundle size
uses: siddharthkp/bundlesize@v2
with:
files: 'dist/*.js'
maxSize: '250KB'
Caching Strategy
// Cache layers
const cacheStrategy = {
// L1: In-memory (fastest, smallest)
memory: new LRUCache({ max: 1000, ttl: 60000 }),
// L2: Redis (fast, larger)
redis: new Redis({ maxRetriesPerRequest: 3 }),
// L3: CDN (edge caching)
cdn: {
cacheControl: 'public, max-age=31536000, immutable', // Static assets
staleWhileRevalidate: 'public, max-age=60, stale-while-revalidate=600', // API
},
};
async function getCachedData<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
// Check memory
const memoryHit = cacheStrategy.memory.get(key);
if (memoryHit) return memoryHit as T;
// Check Redis
const redisHit = await cacheStrategy.redis.get(key);
if (redisHit) {
const data = JSON.parse(redisHit);
cacheStrategy.memory.set(key, data);
return data;
}
// Fetch and cache
const data = await fetcher();
cacheStrategy.memory.set(key, data);
await cacheStrategy.redis.setex(key, 300, JSON.stringify(data));
return data;
}
Monitoring Metrics
| Metric |
Target |
| LCP |
< 2.5s |
| INP |
< 200ms |
| CLS |
< 0.1 |
| TTFB |
< 200ms |
| API p95 latency |
< 500ms |
| Database query time |
< 100ms |
| Cache hit rate |
> 90% |
Production Checklist
Reference Documentation
- Bundle Analysis
- Caching Strategies
1---2name: performance3description: Web performance optimization. Covers frontend, backend, and database optimization. Use for performance reviews. USE WHEN: user mentions "performance", "slow", "optimization", "Core Web Vitals", "LCP", "INP", "CLS", "bundle size", "lazy load", "caching", "N+1 query", "memory leak", asks about "how to speed up", "improve performance", "reduce load time", "database optimization" DO NOT USE FOR: Algorithm complexity - use computer science fundamentals, Code quality/readability - use `clean-code` instead, Security optimization - use security-specific skills4---5# Performance Optimization67> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `performance` for comprehensive documentation.89## When NOT to Use This Skill1011This skill focuses on runtime performance optimization. Do NOT use for:1213- **Algorithm optimization** - Use computer science/data structures fundamentals14- **Code readability** - Use `clean-code` skill (don't sacrifice readability for micro-optimizations)15- **Build time optimization** - Use build tool specific skills (Vite, Webpack, etc.)16- **Developer experience** - Use DX-focused skills and tooling17- **Security hardening** - Use security-specific skills (performance != security)1819## Anti-Patterns2021| Anti-Pattern | Why It's Bad | Performance Solution |22|--------------|--------------|---------------------|23| **Premature Optimization** | Waste time on non-bottlenecks | Measure first, optimize bottlenecks only |24| **SELECT *** | Fetches unnecessary data | Select only needed columns |25| **N+1 Queries** | Multiple DB roundtrips | Use joins or eager loading |26| **No Caching** | Repeated expensive computations | Cache at appropriate layer (memory, Redis, CDN) |27| **Blocking Operations** | Holds up main thread | Use async/background jobs |28| **Large Bundle** | Slow initial load | Code splitting, lazy loading |29| **No Image Optimization** | Huge assets over network | Compress, modern formats (WebP, AVIF), lazy load |30| **Missing Indexes** | Full table scans | Add indexes on queried columns |31| **Memory Leaks** | Unbounded growth | Clean up listeners, close connections, clear refs |32| **Synchronous I/O** | Blocks event loop | Use async I/O operations |3334## Quick Troubleshooting3536| Issue | Diagnostic | Solution |37|-------|------------|----------|38| **Slow page load** | Check Network tab | Optimize images, enable compression, use CDN |39| **Poor LCP** | Lighthouse audit | Preload critical resources, optimize largest element |40| **High INP** | Performance profiler | Debounce handlers, use web workers, reduce JS |41| **Layout shifts (CLS)** | Layout Shift Regions | Set dimensions on images/embeds, avoid dynamic content |42| **Slow API response** | APM tools, logging | Add database indexes, cache responses, optimize queries |43| **High memory usage** | Memory profiler | Fix leaks, clear intervals/listeners, use weak refs |44| **Large bundle** | Bundle analyzer | Code split, tree shake, lazy load routes |45| **Slow database query** | EXPLAIN ANALYZE | Add indexes, rewrite query, partition table |4647## Frontend Performance4849### Core Web Vitals5051| Metric | Target | Measurement |52|--------|--------|-------------|53| LCP (Largest Contentful Paint) | < 2.5s | Largest visible element |54| INP (Interaction to Next Paint) | < 200ms | Input responsiveness |55| CLS (Cumulative Layout Shift) | < 0.1 | Visual stability |5657### Optimization Techniques5859```tsx60// Code splitting61const Dashboard = lazy(() => import('./Dashboard'));6263// Image optimization64<Image65 src="/hero.jpg"66 width={1200}67 height={600}68 priority // Above fold69 placeholder="blur"70/>7172// Memoization73const MemoizedComponent = memo(ExpensiveComponent);74const memoizedValue = useMemo(() => computeExpensive(a, b), [a, b]);75const memoizedFn = useCallback(() => handleClick(id), [id]);7677// Virtual lists for long lists78<VirtualList items={items} itemHeight={50} />79```8081## Backend Performance8283```typescript84// N+1 prevention85const usersWithPosts = await prisma.user.findMany({86 include: { posts: true } // Single query with join87});8889// Caching90const cached = await redis.get(key);91if (cached) return JSON.parse(cached);92const data = await db.query();93await redis.setex(key, 3600, JSON.stringify(data));9495// Connection pooling96const pool = new Pool({ max: 20 });9798// Async processing99await queue.add('sendEmail', { userId });100```101102## Database Performance103104```sql105-- Use EXPLAIN ANALYZE106EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'x';107108-- Add indexes for frequently queried columns109CREATE INDEX idx_users_email ON users(email);110111-- Partial indexes112CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;113114-- Avoid SELECT *115SELECT id, name, email FROM users;116117-- Pagination118SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 0;119```120121## Checklist122123| Area | Check |124|------|-------|125| Images | Optimized, lazy loaded, proper format |126| JS Bundle | Code split, tree shaken, minified |127| CSS | Critical CSS inline, unused removed |128| Fonts | Preloaded, subset, font-display |129| Caching | CDN, browser cache, API cache |130| Database | Indexes, query optimization |131132## Production Readiness133134### Monitoring Setup135136```typescript137// Web Vitals reporting138import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';139140function sendToAnalytics(metric: Metric) {141 const body = JSON.stringify({142 name: metric.name,143 value: metric.value,144 id: metric.id,145 page: window.location.pathname,146 });147148 // Use sendBeacon for reliability149 navigator.sendBeacon('/analytics', body);150}151152onCLS(sendToAnalytics);153onINP(sendToAnalytics);154onLCP(sendToAnalytics);155onFCP(sendToAnalytics);156onTTFB(sendToAnalytics);157```158159### Performance Budget160161```javascript162// webpack.config.js or vite.config.ts163{164 performance: {165 maxAssetSize: 250000, // 250KB166 maxEntrypointSize: 500000, // 500KB167 hints: 'error',168 },169}170171// Lighthouse CI budget172// lighthouserc.js173module.exports = {174 ci: {175 collect: {176 url: ['http://localhost:3000/'],177 },178 assert: {179 assertions: {180 'categories:performance': ['error', { minScore: 0.9 }],181 'first-contentful-paint': ['error', { maxNumericValue: 2000 }],182 'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],183 'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],184 'total-blocking-time': ['error', { maxNumericValue: 300 }],185 },186 },187 },188};189```190191### Backend Optimization192193```typescript194// Response compression195import compression from 'compression';196app.use(compression({ threshold: 1024 }));197198// Response caching headers199function setCacheHeaders(res: Response, maxAge: number) {200 res.setHeader('Cache-Control', `public, max-age=${maxAge}, stale-while-revalidate=${maxAge * 2}`);201 res.setHeader('Vary', 'Accept-Encoding');202}203204// Streaming responses205async function streamLargeData(res: Response) {206 const stream = db.users.findMany().cursor();207208 res.setHeader('Content-Type', 'application/json');209 res.write('[');210211 let first = true;212 for await (const user of stream) {213 if (!first) res.write(',');214 res.write(JSON.stringify(user));215 first = false;216 }217218 res.write(']');219 res.end();220}221222// Query optimization223const users = await prisma.user.findMany({224 select: { id: true, name: true, email: true }, // Only needed fields225 where: { isActive: true },226 take: 20,227 orderBy: { createdAt: 'desc' },228});229```230231### Database Optimization232233```sql234-- Composite indexes for common queries235CREATE INDEX idx_users_active_created236ON users(is_active, created_at DESC)237WHERE is_active = true;238239-- Query analysis240EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)241SELECT * FROM users WHERE email = 'test@example.com';242243-- Connection pooling configuration244-- pgbouncer.ini245[pgbouncer]246pool_mode = transaction247default_pool_size = 20248max_client_conn = 100249```250251### CI Performance Testing252253```yaml254# .github/workflows/performance.yml255name: Performance256257on:258 pull_request:259 branches: [main]260261jobs:262 lighthouse:263 runs-on: ubuntu-latest264 steps:265 - uses: actions/checkout@v4266267 - name: Build268 run: npm run build269270 - name: Run Lighthouse CI271 uses: treosh/lighthouse-ci-action@v10272 with:273 configPath: ./lighthouserc.js274 uploadArtifacts: true275276 bundle-size:277 runs-on: ubuntu-latest278 steps:279 - uses: actions/checkout@v4280281 - name: Build282 run: npm run build283284 - name: Check bundle size285 uses: siddharthkp/bundlesize@v2286 with:287 files: 'dist/*.js'288 maxSize: '250KB'289```290291### Caching Strategy292293```typescript294// Cache layers295const cacheStrategy = {296 // L1: In-memory (fastest, smallest)297 memory: new LRUCache({ max: 1000, ttl: 60000 }),298299 // L2: Redis (fast, larger)300 redis: new Redis({ maxRetriesPerRequest: 3 }),301302 // L3: CDN (edge caching)303 cdn: {304 cacheControl: 'public, max-age=31536000, immutable', // Static assets305 staleWhileRevalidate: 'public, max-age=60, stale-while-revalidate=600', // API306 },307};308309async function getCachedData<T>(key: string, fetcher: () => Promise<T>): Promise<T> {310 // Check memory311 const memoryHit = cacheStrategy.memory.get(key);312 if (memoryHit) return memoryHit as T;313314 // Check Redis315 const redisHit = await cacheStrategy.redis.get(key);316 if (redisHit) {317 const data = JSON.parse(redisHit);318 cacheStrategy.memory.set(key, data);319 return data;320 }321322 // Fetch and cache323 const data = await fetcher();324 cacheStrategy.memory.set(key, data);325 await cacheStrategy.redis.setex(key, 300, JSON.stringify(data));326327 return data;328}329```330331### Monitoring Metrics332333| Metric | Target |334|--------|--------|335| LCP | < 2.5s |336| INP | < 200ms |337| CLS | < 0.1 |338| TTFB | < 200ms |339| API p95 latency | < 500ms |340| Database query time | < 100ms |341| Cache hit rate | > 90% |342343### Production Checklist344345- [ ] Core Web Vitals monitored346- [ ] Performance budget set347- [ ] Lighthouse CI in pipeline348- [ ] Bundle size monitoring349- [ ] Image optimization350- [ ] Code splitting enabled351- [ ] Compression enabled352- [ ] Caching strategy defined353- [ ] Database indexes optimized354- [ ] CDN configured355356## Reference Documentation357- [Bundle Analysis](quick-ref/bundle.md)358- [Caching Strategies](quick-ref/caching.md)