performance-reviewer
Review code for performance issues and optimization opportunities. Use when someone needs to check for N+1 queries, unnecessary re-renders, memory leaks, inefficient algorithms, missing indexes, or bundle size regressions. Trigger words: performance review, slow query, N+1, memory leak, bundle size, latency, optimization, re-render, Big O.
Usage
Getting Started
- Install the skill using the command above
- Open your AI coding agent (Claude Code, Codex, Gemini CLI, or Cursor)
- Reference the skill in your prompt
- The AI will use the skill's capabilities automatically
Example Prompts
- "Review the open pull requests and summarize what needs attention"
- "Generate a changelog from the last 20 commits on the main branch"
Documentation
Overview
This skill analyzes code changes for performance regressions and optimization opportunities. It catches common issues like N+1 database queries, unnecessary re-renders in React components, missing database indexes, unoptimized loops, and bundle size increases before they reach production.
Instructions
Analyzing a Diff or PR
- Get the diff:
git diff main...HEADorgit diff <base>...<head> - For each changed file, evaluate against these performance categories:
Database & Queries:
- Look for queries inside loops (N+1 pattern)
- Check for missing
WHEREclauses or full table scans - Identify missing indexes on columns used in
WHERE,JOIN, orORDER BY - Flag
SELECT *when only specific columns are needed - Watch for unbounded queries without
LIMIT
Frontend & Rendering:
- React: Check for missing
useMemo/useCallbackon expensive computations passed as props - Look for state updates that trigger unnecessary re-renders of large component trees
- Flag inline object/array creation in render (creates new reference every render)
- Check for large bundle imports (
import moment→ suggestdayjs)
Algorithm & Data Structures:
- Flag O(n²) or worse algorithms when O(n log n) alternatives exist
- Look for repeated array searches that should use a Set or Map
- Identify string concatenation in loops (suggest StringBuilder/join)
Memory & Resources:
- Check for missing cleanup in
useEffect(event listeners, intervals, subscriptions) - Look for growing arrays/objects that are never trimmed
- Flag missing connection pool limits or unclosed file handles
Network & I/O:
- Identify sequential API calls that could be parallelized (
Promise.all) - Check for missing pagination on list endpoints
- Flag missing caching for expensive or repeated operations
Output Format
For each issue found, report:
- File and line number
- Category (Database, Frontend, Algorithm, Memory, Network)
- Severity (Critical, Warning, Info)
- What's wrong (specific description)
- Suggested fix (concrete code suggestion)
Severity Guidelines
- Critical: Will cause visible degradation in production (N+1 in a loop, O(n²) on large datasets, memory leak)
- Warning: May cause issues at scale (missing index, unnecessary re-renders, sequential API calls)
- Info: Optimization opportunity, not urgent (bundle size suggestion, minor refactor)
Examples
Example 1: N+1 Query Detection
Input diff:
# api/views.py
def list_orders(request):
orders = Order.objects.filter(user=request.user)
result = []
for order in orders:
items = OrderItem.objects.filter(order=order) # ← query per order
result.append({
"id": order.id,
"total": order.total,
"items": [{"name": i.product.name, "qty": i.quantity} for i in items]
})
return JsonResponse(result, safe=False)
Output:
🔴 Critical — Database N+1 Query
File: api/views.py:5-8
Issue: OrderItem query executes once per order, plus each i.product.name
triggers another query. For 50 orders with 3 items each = 201 queries.
Fix: Use select_related and prefetch_related:
orders = Order.objects.filter(user=request.user).prefetch_related(
Prefetch('items', queryset=OrderItem.objects.select_related('product'))
)
Result: Reduces to 3 queries regardless of order count.
Example 2: React Re-render Issue
Input diff:
function UserList({ users, onSelect }) {
return (
<div>
{users.map(user => (
<UserCard
key={user.id}
user={user}
style={{ padding: 16, margin: 8 }}
onClick={() => onSelect(user.id)}
/>
))}
</div>
);
}
Output:
🟡 Warning — Unnecessary Re-renders
File: components/UserList.jsx:6-7
Issue: Inline object `style={{ padding: 16, margin: 8 }}` and arrow function
`() => onSelect(user.id)` create new references every render, causing
all UserCard components to re-render even when users haven't changed.
Fix:
const cardStyle = useMemo(() => ({ padding: 16, margin: 8 }), []);
const handleClick = useCallback((id) => onSelect(id), [onSelect]);
// Then: style={cardStyle} onClick={() => handleClick(user.id)}
Impact: With 100+ users, prevents ~100 unnecessary DOM diffing operations per parent render.
Guidelines
- Focus on issues introduced by the diff, not pre-existing problems
- Prioritize Critical issues — don't bury them in a list of Info suggestions
- Always provide concrete fix suggestions, not just "optimize this"
- Consider the scale: an O(n²) loop on a 5-element array is fine; on user-generated data it's not
- When suggesting caching, specify what to cache and invalidation strategy
- Don't flag micro-optimizations that harm readability for negligible gain
Information
- Version
- 1.0.0
- Author
- terminal-skills
- Category
- Development
- License
- Apache-2.0