DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Finding and Fixing Performance Bottlenecks with Claude Code

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]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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 } }))
);
Enter fullscreen mode Exit fullscreen mode

Fix:

// Single query
const users = await prisma.user.findMany({
  include: { posts: true }
});
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

Before:

const user = await getUser(id);
const orders = await getOrders(id);
const recommendations = await getRecommendations(id);
Enter fullscreen mode Exit fullscreen mode

After:

const [user, orders, recommendations] = await Promise.all([
  getUser(id),
  getOrders(id),
  getRecommendations(id).catch(() => []), // graceful failure
]);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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?
Enter fullscreen mode Exit fullscreen mode

Code Review Pack (¥980) includes /code-review which automatically checks for N+1 patterns, missing LIMIT clauses, and SELECT *.

👉 prompt-works.jp

Myouga (@myougatheaxo) — Security-focused Claude Code engineer.

Top comments (0)