Performance optimizer skill
Identifies and fixes performance bottlenecks in code, databases, and APIs.
by davila7·MIT license·★ 32,299 Stars on the repo·GitHub ↗
npx degit davila7/claude-code-templates/cli-tool/components/skills/productivity/performance-optimizer#main ~/.claude/skills/performance-optimizerChecked ·commit main
Files of Performance optimizer
Show the full text390 lines
Performance Optimizer
Find and fix performance bottlenecks. Measure, optimize, verify. Make it fast.
When to Use This Skill
- App is slow or laggy
- User complains about performance
- Page load times are high
- API responses are slow
- Database queries take too long
- User mentions "slow", "lag", "performance", or "optimize"
The Optimization Process
1. Measure First
Never optimize without measuring:
// Measure execution time
console.time('operation');
await slowOperation();
console.timeEnd('operation'); // operation: 2341ms
What to measure:
- Page load time
- API response time
- Database query time
- Function execution time
- Memory usage
- Network requests
2. Find the Bottleneck
Use profiling tools to find the slow parts:
Browser:
DevTools → Performance tab → Record → Stop
Look for long tasks (red bars)
Node.js:
node --prof app.js
node --prof-process isolate-*.log > profile.txt
Database:
EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
3. Optimize
Fix the slowest thing first (biggest impact).
Common Optimizations
Database Queries
Problem: N+1 Queries
// Bad: N+1 queries
const users = await db.users.find();
for (const user of users) {
user.posts = await db.posts.find({ userId: user.id }); // N queries
}
// Good: Single query with JOIN
const users = await db.users.find()
.populate('posts'); // 1 query
Problem: Missing Index
-- Check slow query
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
-- Shows: Seq Scan (bad)
-- Add index
CREATE INDEX idx_users_email ON users(email);
-- Check again
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
-- Shows: Index Scan (good)
**Problem: SELECT ***
// Bad: Fetches all columns
const users = await db.query('SELECT * FROM users');
// Good: Only needed columns
const users = await db.query('SELECT id, name, email FROM users');
Problem: No Pagination
// Bad: Returns all records
const users = await db.users.find();
// Good: Paginated
const users = await db.users.find()
.limit(20)
.skip((page - 1) * 20);
API Performance
Problem: No Caching
// Bad: Hits database every time
app.get('/api/stats', async (req, res) => {
const stats = await db.stats.calculate(); // Slow
res.json(stats);
});
// Good: Cache for 5 minutes
const cache = new Map();
app.get('/api/stats', async (req, res) => {
const cached = cache.get('stats');
if (cached && Date.now() - cached.time < 300000) {
return res.json(cached.data);
}
const stats = await db.stats.calculate();
cache.set('stats', { data: stats, time: Date.now() });
res.json(stats);
});
Problem: Sequential Operations
// Bad: Sequential (slow)
const user = await getUser(id);
const posts = await getPosts(id);
const comments = await getComments(id);
// Total: 300ms + 200ms + 150ms = 650ms
// Good: Parallel (fast)
const [user, posts, comments] = await Promise.all([
getUser(id),
getPosts(id),
getComments(id)
]);
// Total: max(300ms, 200ms, 150ms) = 300ms
Problem: Large Payloads
// Bad: Returns everything
res.json(users); // 5MB response
// Good: Only needed fields
res.json(users.map(u => ({
id: u.id,
name: u.name,
email: u.email
}))); // 500KB response
Frontend Performance
Problem: Unnecessary Re-renders
// Bad: Re-renders on every parent update
function UserList({ users }) {
return users.map(user => <UserCard user={user} />);
}
// Good: Memoized
const UserCard = React.memo(({ user }) => {
return <div>{user.name}</div>;
});
Problem: Large Bundle
// Bad: Imports entire library
import _ from 'lodash'; // 70KB
// Good: Import only what you need
import debounce from 'lodash/debounce'; // 2KB
Problem: No Code Splitting
// Bad: Everything in one bundle
import HeavyComponent from './HeavyComponent';
// Good: Lazy load
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
Problem: Unoptimized Images
<!-- Bad: Large image -->
<img src="photo.jpg" /> <!-- 5MB -->
<!-- Good: Optimized and responsive -->
<img
src="photo-small.webp"
srcset="photo-small.webp 400w, photo-large.webp 800w"
loading="lazy"
width="400"
height="300"
/> <!-- 50KB -->
Algorithm Optimization
Problem: Inefficient Algorithm
// Bad: O(n²) - nested loops
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) duplicates.push(arr[i]);
}
}
return duplicates;
}
// Good: O(n) - single pass with Set
function findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) duplicates.add(item);
seen.add(item);
}
return Array.from(duplicates);
}
Problem: Repeated Calculations
// Bad: Calculates every time
function getTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// Called 100 times in render
// Good: Memoized
const getTotal = useMemo(() => {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [items]);
Memory Optimization
Problem: Memory Leak
// Bad: Event listener not cleaned up
useEffect(() => {
window.addEventListener('scroll', handleScroll);
// Memory leak!
}, []);
// Good: Cleanup
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
Problem: Large Data in Memory
// Bad: Loads entire file into memory
const data = fs.readFileSync('huge-file.txt'); // 1GB
// Good: Stream it
const stream = fs.createReadStream('huge-file.txt');
stream.on('data', chunk => process(chunk));
Measuring Impact
Always measure before and after:
// Before optimization
console.time('query');
const users = await db.users.find();
console.timeEnd('query');
// query: 2341ms
// After optimization (added index)
console.time('query');
const users = await db.users.find();
console.timeEnd('query');
// query: 23ms
// Improvement: 100x faster!
Performance Budgets
Set targets:
Page Load: < 2 seconds
API Response: < 200ms
Database Query: < 50ms
Bundle Size: < 200KB
Time to Interactive: < 3 seconds
Tools
Browser:
- Chrome DevTools Performance tab
- Lighthouse (audit)
- Network tab (waterfall)
Node.js:
node --prof(profiling)clinic(diagnostics)autocannon(load testing)
Database:
EXPLAIN ANALYZE(query plans)- Slow query log
- Database profiler
Monitoring:
- New Relic
- Datadog
- Sentry Performance
Quick Wins
Easy optimizations with big impact:
- Add database indexes on frequently queried columns
- Enable gzip compression on server
- Add caching for expensive operations
- Lazy load images and heavy components
- Use CDN for static assets
- Minify and compress JavaScript/CSS
- Remove unused dependencies
- Use pagination instead of loading all data
- Optimize images (WebP, proper sizing)
- Enable HTTP/2 on server
Optimization Checklist
- Measured current performance
- Identified bottleneck
- Applied optimization
- Measured improvement
- Verified functionality still works
- No new bugs introduced
- Documented the change
When NOT to Optimize
- Premature optimization (optimize when it's actually slow)
- Micro-optimizations (save 1ms when page takes 5 seconds)
- Readable code is more important than tiny speed gains
- If it's already fast enough
Key Principles
- Measure before optimizing
- Fix the biggest bottleneck first
- Measure after to prove improvement
- Don't sacrifice readability for tiny gains
- Profile in production-like environment
- Consider the 80/20 rule (20% of code causes 80% of slowness)
Related Skills
@database-design- Query optimization@codebase-audit-pre-push- Code review@bug-hunter- Debugging
| 1 | |
| 2 | name performance-optimizer |
| 3 | description "Identifies and fixes performance bottlenecks in code, databases, and APIs. Measures before and after to prove improvements." |
| 4 | category development |
| 5 | risk safe |
| 6 | source community |
| 7 | date_added "2026-03-05" |
| 8 | |
| 9 | |
| 10 | # Performance Optimizer |
| 11 | |
| 12 | Find and fix performance bottlenecks. Measure, optimize, verify. Make it fast. |
| 13 | |
| 14 | ## When to Use This Skill |
| 15 | |
| 16 | App is slow or laggy |
| 17 | User complains about performance |
| 18 | Page load times are high |
| 19 | API responses are slow |
| 20 | Database queries take too long |
| 21 | User mentions "slow", "lag", "performance", or "optimize" |
| 22 | |
| 23 | ## The Optimization Process |
| 24 | |
| 25 | ### 1. Measure First |
| 26 | |
| 27 | Never optimize without measuring: |
| 28 | |
| 29 | |
| 30 | // Measure execution time |
| 31 | console.time('operation'); |
| 32 | await slowOperation(); |
| 33 | console.timeEnd('operation'); // operation: 2341ms |
| 34 | |
| 35 | |
| 36 | **What to measure:** |
| 37 | Page load time |
| 38 | API response time |
| 39 | Database query time |
| 40 | Function execution time |
| 41 | Memory usage |
| 42 | Network requests |
| 43 | |
| 44 | ### 2. Find the Bottleneck |
| 45 | |
| 46 | Use profiling tools to find the slow parts: |
| 47 | |
| 48 | **Browser:** |
| 49 | |
| 50 | DevTools → Performance tab → Record → Stop |
| 51 | Look for long tasks (red bars) |
| 52 | |
| 53 | |
| 54 | **Node.js:** |
| 55 | |
| 56 | node --prof app.js |
| 57 | node --prof-process isolate-*.log > profile.txt |
| 58 | |
| 59 | |
| 60 | **Database:** |
| 61 | |
| 62 | EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]'; |
| 63 | |
| 64 | |
| 65 | ### 3. Optimize |
| 66 | |
| 67 | Fix the slowest thing first (biggest impact). |
| 68 | |
| 69 | ## Common Optimizations |
| 70 | |
| 71 | ### Database Queries |
| 72 | |
| 73 | **Problem: N+1 Queries** |
| 74 | |
| 75 | // Bad: N+1 queries |
| 76 | const users = await db.users.find(); |
| 77 | for (const user of users) { |
| 78 | user.posts = await db.posts.find({ userId: user.id }); // N queries |
| 79 | } |
| 80 | |
| 81 | // Good: Single query with JOIN |
| 82 | const users = await db.users.find() |
| 83 | .populate('posts'); // 1 query |
| 84 | |
| 85 | |
| 86 | **Problem: Missing Index** |
| 87 | |
| 88 | -- Check slow query |
| 89 | EXPLAIN SELECT * FROM users WHERE email = '[email protected]'; |
| 90 | -- Shows: Seq Scan (bad) |
| 91 | |
| 92 | -- Add index |
| 93 | CREATE INDEX idx_users_email ON users(email); |
| 94 | |
| 95 | -- Check again |
| 96 | EXPLAIN SELECT * FROM users WHERE email = '[email protected]'; |
| 97 | -- Shows: Index Scan (good) |
| 98 | |
| 99 | |
| 100 | **Problem: SELECT *** |
| 101 | |
| 102 | // Bad: Fetches all columns |
| 103 | const users = await db.query('SELECT * FROM users'); |
| 104 | |
| 105 | // Good: Only needed columns |
| 106 | const users = await db.query('SELECT id, name, email FROM users'); |
| 107 | |
| 108 | |
| 109 | **Problem: No Pagination** |
| 110 | |
| 111 | // Bad: Returns all records |
| 112 | const users = await db.users.find(); |
| 113 | |
| 114 | // Good: Paginated |
| 115 | const users = await db.users.find() |
| 116 | .limit(20) |
| 117 | .skip((page - 1) * 20); |
| 118 | |
| 119 | |
| 120 | ### API Performance |
| 121 | |
| 122 | **Problem: No Caching** |
| 123 | |
| 124 | // Bad: Hits database every time |
| 125 | app.get('/api/stats', async (req, res) => { |
| 126 | const stats = await db.stats.calculate(); // Slow |
| 127 | res.json(stats); |
| 128 | }); |
| 129 | |
| 130 | // Good: Cache for 5 minutes |
| 131 | const cache = new Map(); |
| 132 | app.get('/api/stats', async (req, res) => { |
| 133 | const cached = cache.get('stats'); |
| 134 | if (cached && Date.now() - cached.time < 300000) { |
| 135 | return res.json(cached.data); |
| 136 | } |
| 137 | |
| 138 | const stats = await db.stats.calculate(); |
| 139 | cache.set('stats', { data: stats, time: Date.now() }); |
| 140 | res.json(stats); |
| 141 | }); |
| 142 | |
| 143 | |
| 144 | **Problem: Sequential Operations** |
| 145 | |
| 146 | // Bad: Sequential (slow) |
| 147 | const user = await getUser(id); |
| 148 | const posts = await getPosts(id); |
| 149 | const comments = await getComments(id); |
| 150 | // Total: 300ms + 200ms + 150ms = 650ms |
| 151 | |
| 152 | // Good: Parallel (fast) |
| 153 | const [user, posts, comments] = await Promise.all([ |
| 154 | getUser(id), |
| 155 | getPosts(id), |
| 156 | getComments(id) |
| 157 | ]); |
| 158 | // Total: max(300ms, 200ms, 150ms) = 300ms |
| 159 | |
| 160 | |
| 161 | **Problem: Large Payloads** |
| 162 | |
| 163 | // Bad: Returns everything |
| 164 | res.json(users); // 5MB response |
| 165 | |
| 166 | // Good: Only needed fields |
| 167 | res.json(users.map(u => ({ |
| 168 | id: u.id, |
| 169 | name: u.name, |
| 170 | email: u.email |
| 171 | }))); // 500KB response |
| 172 | |
| 173 | |
| 174 | ### Frontend Performance |
| 175 | |
| 176 | **Problem: Unnecessary Re-renders** |
| 177 | |
| 178 | // Bad: Re-renders on every parent update |
| 179 | function UserList({ users }) { |
| 180 | return users.map(user => <UserCard user={user} />); |
| 181 | } |
| 182 | |
| 183 | // Good: Memoized |
| 184 | const UserCard = React.memo(({ user }) => { |
| 185 | return <div>{user.name}</div>; |
| 186 | }); |
| 187 | |
| 188 | |
| 189 | **Problem: Large Bundle** |
| 190 | |
| 191 | // Bad: Imports entire library |
| 192 | import _ from 'lodash'; // 70KB |
| 193 | |
| 194 | // Good: Import only what you need |
| 195 | import debounce from 'lodash/debounce'; // 2KB |
| 196 | |
| 197 | |
| 198 | **Problem: No Code Splitting** |
| 199 | |
| 200 | // Bad: Everything in one bundle |
| 201 | import HeavyComponent from './HeavyComponent'; |
| 202 | |
| 203 | // Good: Lazy load |
| 204 | const HeavyComponent = React.lazy(() => import('./HeavyComponent')); |
| 205 | |
| 206 | |
| 207 | **Problem: Unoptimized Images** |
| 208 | |
| 209 | <!-- Bad: Large image --> |
| 210 | <img src="photo.jpg" /> <!-- 5MB --> |
| 211 | |
| 212 | <!-- Good: Optimized and responsive --> |
| 213 | <img |
| 214 | src="photo-small.webp" |
| 215 | srcset="photo-small.webp 400w, photo-large.webp 800w" |
| 216 | loading="lazy" |
| 217 | width="400" |
| 218 | height="300" |
| 219 | /> <!-- 50KB --> |
| 220 | |
| 221 | |
| 222 | ### Algorithm Optimization |
| 223 | |
| 224 | **Problem: Inefficient Algorithm** |
| 225 | |
| 226 | // Bad: O(n²) - nested loops |
| 227 | function findDuplicates(arr) { |
| 228 | const duplicates = []; |
| 229 | for (let i = 0; i < arr.length; i++) { |
| 230 | for (let j = i + 1; j < arr.length; j++) { |
| 231 | if (arr[i] === arr[j]) duplicates.push(arr[i]); |
| 232 | } |
| 233 | } |
| 234 | return duplicates; |
| 235 | } |
| 236 | |
| 237 | // Good: O(n) - single pass with Set |
| 238 | function findDuplicates(arr) { |
| 239 | const seen = new Set(); |
| 240 | const duplicates = new Set(); |
| 241 | for (const item of arr) { |
| 242 | if (seen.has(item)) duplicates.add(item); |
| 243 | seen.add(item); |
| 244 | } |
| 245 | return Array.from(duplicates); |
| 246 | } |
| 247 | |
| 248 | |
| 249 | **Problem: Repeated Calculations** |
| 250 | |
| 251 | // Bad: Calculates every time |
| 252 | function getTotal(items) { |
| 253 | return items.reduce((sum, item) => sum + item.price * item.quantity, 0); |
| 254 | } |
| 255 | // Called 100 times in render |
| 256 | |
| 257 | // Good: Memoized |
| 258 | const getTotal = useMemo(() => { |
| 259 | return items.reduce((sum, item) => sum + item.price * item.quantity, 0); |
| 260 | }, [items]); |
| 261 | |
| 262 | |
| 263 | ### Memory Optimization |
| 264 | |
| 265 | **Problem: Memory Leak** |
| 266 | |
| 267 | // Bad: Event listener not cleaned up |
| 268 | useEffect(() => { |
| 269 | window.addEventListener('scroll', handleScroll); |
| 270 | // Memory leak! |
| 271 | }, []); |
| 272 | |
| 273 | // Good: Cleanup |
| 274 | useEffect(() => { |
| 275 | window.addEventListener('scroll', handleScroll); |
| 276 | return () => window.removeEventListener('scroll', handleScroll); |
| 277 | }, []); |
| 278 | |
| 279 | |
| 280 | **Problem: Large Data in Memory** |
| 281 | |
| 282 | // Bad: Loads entire file into memory |
| 283 | const data = fs.readFileSync('huge-file.txt'); // 1GB |
| 284 | |
| 285 | // Good: Stream it |
| 286 | const stream = fs.createReadStream('huge-file.txt'); |
| 287 | stream.on('data', chunk => process(chunk)); |
| 288 | |
| 289 | |
| 290 | ## Measuring Impact |
| 291 | |
| 292 | Always measure before and after: |
| 293 | |
| 294 | |
| 295 | // Before optimization |
| 296 | console.time('query'); |
| 297 | const users = await db.users.find(); |
| 298 | console.timeEnd('query'); |
| 299 | // query: 2341ms |
| 300 | |
| 301 | // After optimization (added index) |
| 302 | console.time('query'); |
| 303 | const users = await db.users.find(); |
| 304 | console.timeEnd('query'); |
| 305 | // query: 23ms |
| 306 | |
| 307 | // Improvement: 100x faster! |
| 308 | |
| 309 | |
| 310 | ## Performance Budgets |
| 311 | |
| 312 | Set targets: |
| 313 | |
| 314 | |
| 315 | Page Load: < 2 seconds |
| 316 | API Response: < 200ms |
| 317 | Database Query: < 50ms |
| 318 | Bundle Size: < 200KB |
| 319 | Time to Interactive: < 3 seconds |
| 320 | |
| 321 | |
| 322 | ## Tools |
| 323 | |
| 324 | **Browser:** |
| 325 | Chrome DevTools Performance tab |
| 326 | Lighthouse (audit) |
| 327 | Network tab (waterfall) |
| 328 | |
| 329 | **Node.js:** |
| 330 | `node --prof` (profiling) |
| 331 | `clinic` (diagnostics) |
| 332 | `autocannon` (load testing) |
| 333 | |
| 334 | **Database:** |
| 335 | `EXPLAIN ANALYZE` (query plans) |
| 336 | Slow query log |
| 337 | Database profiler |
| 338 | |
| 339 | **Monitoring:** |
| 340 | New Relic |
| 341 | Datadog |
| 342 | Sentry Performance |
| 343 | |
| 344 | ## Quick Wins |
| 345 | |
| 346 | Easy optimizations with big impact: |
| 347 | |
| 348 | **Add database indexes** on frequently queried columns |
| 349 | **Enable gzip compression** on server |
| 350 | **Add caching** for expensive operations |
| 351 | **Lazy load** images and heavy components |
| 352 | **Use CDN** for static assets |
| 353 | **Minify and compress** JavaScript/CSS |
| 354 | **Remove unused dependencies** |
| 355 | **Use pagination** instead of loading all data |
| 356 | **Optimize images** (WebP, proper sizing) |
| 357 | **Enable HTTP/2** on server |
| 358 | |
| 359 | ## Optimization Checklist |
| 360 | |
| 361 | [ ] Measured current performance |
| 362 | [ ] Identified bottleneck |
| 363 | [ ] Applied optimization |
| 364 | [ ] Measured improvement |
| 365 | [ ] Verified functionality still works |
| 366 | [ ] No new bugs introduced |
| 367 | [ ] Documented the change |
| 368 | |
| 369 | ## When NOT to Optimize |
| 370 | |
| 371 | Premature optimization (optimize when it's actually slow) |
| 372 | Micro-optimizations (save 1ms when page takes 5 seconds) |
| 373 | Readable code is more important than tiny speed gains |
| 374 | If it's already fast enough |
| 375 | |
| 376 | ## Key Principles |
| 377 | |
| 378 | Measure before optimizing |
| 379 | Fix the biggest bottleneck first |
| 380 | Measure after to prove improvement |
| 381 | Don't sacrifice readability for tiny gains |
| 382 | Profile in production-like environment |
| 383 | Consider the 80/20 rule (20% of code causes 80% of slowness) |
| 384 | |
| 385 | ## Related Skills |
| 386 | |
| 387 | `@database-design` - Query optimization |
| 388 | `@codebase-audit-pre-push` - Code review |
| 389 | `@bug-hunter` - Debugging |
| 390 |
Discussion
Alternatives
Browse more free Claude skills or everything in Development.