Performance Reviewer
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...HEAD or git 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
WHERE clauses or full table scans
- Identify missing indexes on columns used in
WHERE, JOIN, or ORDER BY
- Flag
SELECT * when only specific columns are needed
- Watch for unbounded queries without
LIMIT
Frontend & Rendering:
- React: Check for missing
useMemo/useCallback on 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 → suggest dayjs)
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 }}
=> 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} => 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
1---2name: performance-reviewer3description: 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.4license: Apache-2.05---67# Performance Reviewer89## Overview1011This 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.1213## Instructions1415### Analyzing a Diff or PR16171. Get the diff: `git diff main...HEAD` or `git diff <base>...<head>`182. For each changed file, evaluate against these performance categories:1920**Database & Queries:**21- Look for queries inside loops (N+1 pattern)22- Check for missing `WHERE` clauses or full table scans23- Identify missing indexes on columns used in `WHERE`, `JOIN`, or `ORDER BY`24- Flag `SELECT *` when only specific columns are needed25- Watch for unbounded queries without `LIMIT`2627**Frontend & Rendering:**28- React: Check for missing `useMemo`/`useCallback` on expensive computations passed as props29- Look for state updates that trigger unnecessary re-renders of large component trees30- Flag inline object/array creation in render (creates new reference every render)31- Check for large bundle imports (`import moment` → suggest `dayjs`)3233**Algorithm & Data Structures:**34- Flag O(n²) or worse algorithms when O(n log n) alternatives exist35- Look for repeated array searches that should use a Set or Map36- Identify string concatenation in loops (suggest StringBuilder/join)3738**Memory & Resources:**39- Check for missing cleanup in `useEffect` (event listeners, intervals, subscriptions)40- Look for growing arrays/objects that are never trimmed41- Flag missing connection pool limits or unclosed file handles4243**Network & I/O:**44- Identify sequential API calls that could be parallelized (`Promise.all`)45- Check for missing pagination on list endpoints46- Flag missing caching for expensive or repeated operations4748### Output Format4950For each issue found, report:51- **File and line number**52- **Category** (Database, Frontend, Algorithm, Memory, Network)53- **Severity** (Critical, Warning, Info)54- **What's wrong** (specific description)55- **Suggested fix** (concrete code suggestion)5657### Severity Guidelines5859- **Critical**: Will cause visible degradation in production (N+1 in a loop, O(n²) on large datasets, memory leak)60- **Warning**: May cause issues at scale (missing index, unnecessary re-renders, sequential API calls)61- **Info**: Optimization opportunity, not urgent (bundle size suggestion, minor refactor)6263## Examples6465### Example 1: N+1 Query Detection6667**Input diff:**68```python69# api/views.py70def list_orders(request):71 orders = Order.objects.filter(user=request.user)72 result = []73 for order in orders:74 items = OrderItem.objects.filter(order=order) # ← query per order75 result.append({76 "id": order.id,77 "total": order.total,78 "items": [{"name": i.product.name, "qty": i.quantity} for i in items]79 })80 return JsonResponse(result, safe=False)81```8283**Output:**84```85🔴 Critical — Database N+1 Query86 File: api/views.py:5-887 Issue: OrderItem query executes once per order, plus each i.product.name88 triggers another query. For 50 orders with 3 items each = 201 queries.89 Fix: Use select_related and prefetch_related:90 orders = Order.objects.filter(user=request.user).prefetch_related(91 Prefetch('items', queryset=OrderItem.objects.select_related('product'))92 )93 Result: Reduces to 3 queries regardless of order count.94```9596### Example 2: React Re-render Issue9798**Input diff:**99```jsx100function UserList({ users, onSelect }) {101 return (102 <div>103 {users.map(user => (104 <UserCard105 key={user.id}106 user={user}107 style={{ padding: 16, margin: 8 }}108 onClick={() => onSelect(user.id)}109 />110 ))}111 </div>112 );113}114```115116**Output:**117```118🟡 Warning — Unnecessary Re-renders119 File: components/UserList.jsx:6-7120 Issue: Inline object `style={{ padding: 16, margin: 8 }}` and arrow function121 `() => onSelect(user.id)` create new references every render, causing122 all UserCard components to re-render even when users haven't changed.123 Fix:124 const cardStyle = useMemo(() => ({ padding: 16, margin: 8 }), []);125 const handleClick = useCallback((id) => onSelect(id), [onSelect]);126 // Then: style={cardStyle} onClick={() => handleClick(user.id)}127 Impact: With 100+ users, prevents ~100 unnecessary DOM diffing operations per parent render.128```129130## Guidelines131132- Focus on issues introduced by the diff, not pre-existing problems133- Prioritize Critical issues — don't bury them in a list of Info suggestions134- Always provide concrete fix suggestions, not just "optimize this"135- Consider the scale: an O(n²) loop on a 5-element array is fine; on user-generated data it's not136- When suggesting caching, specify what to cache and invalidation strategy137- Don't flag micro-optimizations that harm readability for negligible gain