Performance optimization is one of those areas where Claude Code is particularly good — it can spot N+1 queries, inefficient patterns, and missing indexes that are easy to miss in code review.
Here's a structured approach that consistently works.
Step 1: Diagnose Before Optimizing
Never start with "optimize this." Start with diagnosis:
Analyze this code for performance issues. Focus on:
- N+1 query patterns
- Unnecessary data fetching (SELECT *, over-fetching)
- Missing database indexes
- Synchronous operations that could be parallel
- Memory allocation patterns
Do NOT suggest code changes yet. Just list the issues with severity.
[paste the code]
Getting a diagnosis first means you can prioritize what to fix and what to skip.
Step 2: Add Instrumentation
For slow APIs where the bottleneck is unclear:
This API response is taking 2.3 seconds. Target is under 500ms.
Where should I add timing logs to identify the slow operation?
[paste the code]
Claude Code will identify the most valuable points to measure. Add the logs, run the operation, then ask it to interpret the results.
Step 3: N+1 Query Fixes
The most common backend performance issue:
This code has an N+1 query problem. Fix it using a single query with
appropriate joins/eager loading. Keep the return type identical.
// Current (N+1 — runs 1 + N queries)
const users = await prisma.user.findMany();
const result = await Promise.all(
users.map(u => prisma.post.findMany({ where: { userId: u.id } }))
);
Fix:
// Single query
const users = await prisma.user.findMany({
include: { posts: true }
});
For raw SQL:
This query is slow (1.5s on 100k rows). The EXPLAIN plan shows a seq scan.
Suggest index additions that would help without over-indexing.
Query: [paste the query]
Table schemas: [paste CREATE TABLE statements]
Existing indexes: [paste index list]
Step 4: Async Parallelization
This function calls 3 external APIs sequentially. Refactor to call them
in parallel. Handle partial failures gracefully (one API failing
shouldn't fail the entire function).
[paste the function]
Before:
const user = await getUser(id);
const orders = await getOrders(id);
const recommendations = await getRecommendations(id);
After:
const [user, orders, recommendations] = await Promise.all([
getUser(id),
getOrders(id),
getRecommendations(id).catch(() => []), // graceful failure
]);
CLAUDE.md Performance Rules
## Performance Requirements
### Database
- No SELECT * (specify columns)
- No N+1 queries (use include/join for relationships)
- All list queries must have LIMIT (max 1000 rows)
- Index required columns: foreign keys, frequently filtered fields
### API Calls
- Parallel where possible (Promise.all)
- Timeout on all external calls (5s default in src/lib/http.ts)
- Cache responses where appropriate (see src/lib/cache.ts)
### Benchmarks
- API endpoints: P95 < 200ms (simple), P95 < 1000ms (complex)
- Background jobs: complete within job timeout
Hook: Detect N+1 Patterns
# .claude/hooks/check_n1.py
import json, re, sys
data = json.load(sys.stdin)
content = data.get("tool_input", {}).get("content", "") or ""
fp = data.get("tool_input", {}).get("file_path", "")
if not content or not fp.endswith((".ts", ".js")):
sys.exit(0)
# Detect common N+1 patterns
patterns = [
(r'\.map\([^)]+await', "Possible N+1: await inside .map()"),
(r'for.{0,30}(await|findMany|findUnique)', "Possible N+1: DB query in loop"),
(r'SELECT\s+\*', "SELECT * detected — specify columns"),
]
for pattern, msg in patterns:
if re.search(pattern, content, re.DOTALL | re.IGNORECASE):
print(f"[PERF] {msg}", file=sys.stderr)
sys.exit(0)
Reading Performance Data
After adding instrumentation, paste the results back:
Here are the timing results from the instrumented code:
getUser: 12ms
getOrders: 1450ms ← bottleneck
getRecommendations: 45ms
The getOrders function code:
[paste the function]
What's causing the 1.4s execution time and how should I fix it?
Code Review Pack (¥980) includes /code-review which automatically checks for N+1 patterns, missing LIMIT clauses, and SELECT *.
Myouga (@myougatheaxo) — Security-focused Claude Code engineer.
Top comments (0)