review-perf
npx skills add https://github.com/nielsmadan/agentic-coding --skill review-perf
Agent 安装分布
Skill 文档
Review Performance
Performance analysis for common bottlenecks and inefficiencies.
Usage
/review-perf # Review context-related code
/review-perf --staged # Review staged changes
/review-perf --all # Full codebase audit (parallel agents)
Scope
| Flag | Scope | Method |
|---|---|---|
| (none) | Context-related code | Files from the current conversation context: any files the user has discussed, opened, or that you have read/edited in this session. If no conversation context exists, ask the user to specify files or use --staged/--all. |
--staged |
Staged changes | git diff --cached --name-only |
--all |
Full codebase | Glob source files, parallel agents |
Workflow
- Determine scope based on flags (see Scope table above)
- Review each file against all 5 categories in the Performance Checklist below: Algorithmic Complexity, Database/Query Patterns, Memory Management, UI/Render Performance, Network/IO
- Parallelize if scope has >5 files: spawn one sub-agent per category, each scanning all files for that category. Merge results and deduplicate.
- Classify severity for each finding:
- Critical: User-facing slowdown, data loss risk, or resource exhaustion (e.g., memory leak, N+1 on hot path)
- High: Measurable inefficiency on a common code path but not immediately user-visible (e.g., O(n²) on lists typically < 100 items but growing)
- Medium: Suboptimal pattern that could become a problem at scale (e.g., missing pagination, sequential requests that could be parallel)
- Suggestion: Optimization opportunity with marginal current impact
- Report findings grouped by severity using the Output Format below
Performance Checklist
Algorithmic Complexity
O(n²) or Worse:
// BAD: O(n²) nested loops
for (const item of items) {
for (const other of items) {
if (item.id === other.parentId) { ... }
}
}
// GOOD: O(n) with lookup map
const parentMap = new Map(items.map(i => [i.id, i]));
for (const item of items) {
const parent = parentMap.get(item.parentId);
}
Repeated Calculations:
// BAD: Recalculating in loop
items.forEach(item => {
const config = expensiveConfigLookup(); // Called n times
process(item, config);
});
// GOOD: Calculate once
const config = expensiveConfigLookup();
items.forEach(item => process(item, config));
Missing Early Exit:
// BAD: Always iterates entire array
function findUser(users, id) {
let result = null;
users.forEach(u => { if (u.id === id) result = u; });
return result;
}
// GOOD: Exit when found
function findUser(users, id) {
return users.find(u => u.id === id);
}
Database/Query Patterns
N+1 Queries:
// BAD: Query per item
const users = await db.users.findAll();
for (const user of users) {
user.posts = await db.posts.findAll({ where: { userId: user.id } });
}
// GOOD: Single query with include
const users = await db.users.findAll({
include: [{ model: db.posts }]
});
Missing Pagination:
// BAD: Load all records
const allUsers = await db.users.findAll();
// GOOD: Paginate
const users = await db.users.findAll({
limit: 50,
offset: page * 50
});
SELECT * When Few Columns Needed:
-- BAD: Fetching everything
SELECT * FROM users WHERE active = true;
-- GOOD: Only needed columns
SELECT id, name, email FROM users WHERE active = true;
Missing Indexes:
- Columns used in WHERE clauses
- Columns used in ORDER BY
- Foreign key columns
- Columns used in JOIN conditions
Memory Management
Unclosed Resources:
// BAD: Connection never closed
const conn = await db.connect();
const data = await conn.query('...');
// conn stays open
// GOOD: Always close
const conn = await db.connect();
try {
return await conn.query('...');
} finally {
conn.close();
}
Growing Caches:
// BAD: Cache grows forever
const cache = {};
function getValue(key) {
if (!cache[key]) cache[key] = expensiveLookup(key);
return cache[key];
}
// GOOD: LRU or TTL cache
const cache = new LRUCache({ max: 1000 });
Event Listener Leaks:
// BAD: Never removed
useEffect(() => {
window.addEventListener('resize', handler);
}, []);
// GOOD: Cleanup
useEffect(() => {
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
UI/Render Performance
Unnecessary Re-renders (React):
// BAD: New object every render
<Child style={{ color: 'red' }} />
<Child onClick={() => handleClick(id)} />
// GOOD: Memoize
const style = useMemo(() => ({ color: 'red' }), []);
const handleClickMemo = useCallback(() => handleClick(id), [id]);
Missing Virtualization:
// BAD: Render 10,000 items
{items.map(item => <Row key={item.id} {...item} />)}
// GOOD: Use virtualization
<VirtualizedList
data={items}
renderItem={({ item }) => <Row {...item} />}
/>
Blocking Main Thread:
// BAD: Heavy sync computation
function processData(data) {
return data.map(item => expensiveTransform(item)); // Blocks UI
}
// GOOD: Use web worker or chunk
async function processData(data) {
return await worker.process(data);
}
Network/IO
Sequential Requests:
// BAD: Wait for each
const user = await fetchUser(id);
const posts = await fetchPosts(id);
const comments = await fetchComments(id);
// GOOD: Parallel
const [user, posts, comments] = await Promise.all([
fetchUser(id),
fetchPosts(id),
fetchComments(id)
]);
Missing Request Deduplication:
// BAD: Same request multiple times
componentA.fetchUser(123);
componentB.fetchUser(123); // Duplicate request
// GOOD: Cache or dedupe
const { data } = useSWR(`/users/${id}`, fetcher);
Output Format
## Performance Review: {scope}
### Critical (user-facing slowdown)
- {file}:{line} - {issue type}: {description}
**Impact:** {why it matters}
**Fix:** {solution with code example}
### High Priority
- {file}:{line} - {issue}
**Fix:** {solution}
### Medium Priority
- {file} - {issue}
### Suggestions
- {optimization opportunity}
Examples
Staged changes introduce N+1 query:
/review-perf –staged
Reviews staged files and catches a new user list endpoint that queries posts per user in a loop. Reports it as Critical with the impact (“100 users = 101 queries”) and provides a fix using eager loading with include.
Full audit finds memory leak in dashboard:
/review-perf –all
Parallel agents scan the full codebase by category. Finds an event listener in the dashboard component that is never cleaned up on unmount, plus an unbounded in-memory cache growing with every API call.
Troubleshooting
False positive on a rarely-executed code path
Solution: If the flagged code runs only during initialization or in admin-only flows, note the expected data size in a code comment. Re-run the review and the context will help distinguish hot paths from cold ones.
Cannot determine algorithmic complexity without runtime data
Solution: Add a brief comment with the expected input size (e.g., // n is typically < 50) so static analysis can assess impact. For uncertain cases, use /perf-test to measure actual performance with realistic data.
Notes
- Focus on measurable impact, not micro-optimizations
- Consider data size – O(n²) on 10 items is fine, on 10,000 is not
- For
--all, use parallel agents per category - Database issues often have the highest impact
- UI issues matter most for user-facing code