Performance Review
Identify bottlenecks, inefficiencies, and scalability risks in code changes.
Analysis Process
- Read affected files -- understand data access patterns, algorithmic complexity, and resource usage
- Identify N+1 queries -- look for ORM calls inside loops, missing eager loading, unbatched database access
- Check algorithmic complexity -- nested loops over collections, repeated linear scans, unnecessary sorting
- Evaluate memory usage -- large object allocations, unbounded caches, retained references, memory leaks
- Review database patterns -- missing indexes, full table scans, unoptimized joins, excessive round trips
- Check caching -- missing cache layers, cache invalidation issues, redundant computations
- Assess bundle/payload size -- unnecessary imports, large dependencies, uncompressed responses
- Review rendering performance -- unnecessary re-renders, missing memoization, layout thrashing (frontend)
Output Format
Structure findings as:
## Performance Analysis
### Critical Issues
Issues that will cause noticeable degradation at scale.
- [issue] -- where in the code, why it matters, estimated impact
### N+1 Query Detection
| Location | Pattern | Fix |
|----------|---------|-----|
| file:line | Description of the N+1 | Eager load / batch / join |
### Algorithmic Complexity
| Location | Current | Suggested | Why |
|----------|---------|-----------|-----|
| file:line | O(n^2) | O(n) | Description |
### Database Concerns
- Missing indexes, unoptimized queries, excessive round trips
### Memory Concerns
- Unbounded growth, large allocations, retained references
### Caching Opportunities
- Computations or queries that could benefit from caching
### Recommendations
- [recommendation] -- priority (critical/warning/suggestion), estimated impact
Common Patterns to Flag
N+1 Queries
// Bad: N+1 -- one query per user inside loop
const users = await userRepo.find();
const profiles = await Promise.all(users.map(u => profileRepo.findOne({ userId: u.id })));
// Good: Single query with join or batch
const users = await userRepo.find({ relations: ["profile"] });
Unnecessary Re-computation
// Bad: Recomputes on every call
const getExpensiveResult = () => heavyComputation(data);
// Good: Compute once, reuse
const expensiveResult = heavyComputation(data);
Unbounded Collection Growth
// Bad: Cache grows without limit
const cache = new Map();
const get = (key) => { if (!cache.has(key)) cache.set(key, compute(key)); return cache.get(key); };
// Good: LRU or bounded cache
const cache = new LRUCache({ max: 1000 });
Rules
- Focus on the specific changes proposed, not a full performance audit of the entire codebase
- Flag only real performance risks -- do not micro-optimize code that runs once at startup
- Quantify impact where possible (O(n) vs O(n^2), number of database round trips, estimated payload size)
- Distinguish between critical issues (will degrade at scale) and suggestions (marginal improvement)
- If the changes have no performance implications, report "No performance concerns" and explain why
- Always consider the data scale -- an O(n^2) over 5 items is fine, over 10,000 is not