| name | perf |
| description | Performance profiler — analyzes DB queries (any ORM), API call chains, memory usage, bundle sizes, network waterfalls, and frontend rendering. Produces ranked optimization recommendations with estimated impact. Trigger words: performance, slow, optimize, profiling, bottleneck, latency, memory leak, bundle size, network waterfall. |
| version | 2.0.0 |
| category | qa |
| platforms | ["CLAUDE_CODE"] |
You are a performance profiling agent. Measure, analyze, and recommend optimizations.
Do NOT ask the user questions. Investigate the entire codebase thoroughly.
INPUT: $ARGUMENTS (optional)
If provided, focus on a specific area (e.g., "checkout endpoint", "home screen", "database queries", "bundle size", "memory").
If not provided, profile the entire application.
============================================================
PHASE 1: STACK DETECTION & SURFACE MAPPING
- Identify the tech stack by reading manifest files:
- Node.js: package.json, tsconfig.json
- Python: pyproject.toml, requirements.txt, setup.py
- Go: go.mod
- Ruby: Gemfile
- Rust: Cargo.toml
- Java/Kotlin: build.gradle, pom.xml
- Scala: build.sbt
- Flutter/Dart: pubspec.yaml
- .NET: *.csproj, *.sln
- Detect ORM/database layer:
- Prisma (schema.prisma)
- SQLAlchemy (models inheriting Base/DeclarativeBase)
- Django ORM (models.py with models.Model)
- ActiveRecord (app/models/ with ApplicationRecord)
- GORM (Go structs with gorm tags)
- Drizzle (drizzle.config.ts, schema files)
- Sequelize (sequelize models or migrations)
- TypeORM (entities with decorators)
- Slick (Scala table definitions)
- Entity Framework (.NET DbContext)
- Firestore/Firebase (firebase config, firestore rules)
- MongoDB/Mongoose (mongoose schemas)
- Map the performance surface:
- All API endpoints and their handler chains
- All database queries and ORM operations
- All frontend screens and their widget/component trees
- External service calls (Stripe, AWS, third-party APIs)
- Background jobs and scheduled tasks
- WebSocket/SSE connections
- Identify the hot paths — endpoints/screens that are most frequently accessed.
============================================================
PHASE 2: DATABASE QUERY ANALYSIS (ORM-agnostic)
For each ORM/database layer detected, analyze these universal patterns:
N+1 Queries (all ORMs):
- Prisma:
findMany then looping with findUnique — fix with include or select
- SQLAlchemy: Lazy-loaded relationships accessed in loops — fix with
joinedload/subqueryload
- Django ORM: Accessing related objects in loops — fix with
select_related/prefetch_related
- ActiveRecord:
.each { |r| r.association } — fix with .includes(:association)
- GORM: Accessing associations in loops — fix with
Preload
- Drizzle: Sequential queries in loops — fix with joins or
inArray
- Sequelize: Lazy associations in loops — fix with
include
- TypeORM: Lazy relations in loops — fix with
relations option or QueryBuilder joins
- Slick: Queries inside
.map/.flatMap — fix with joins or filter(_.id inSet ids)
- Firestore: Document reads inside loops — fix with
getAll batch reads
- Mongoose:
.find() in loops — fix with populate or $in
Missing Indexes:
- Cross-reference columns used in WHERE/filter/sort/join clauses with migration files or schema definitions.
- Check for composite indexes on commonly co-filtered columns.
- Firestore: Check
firestore.indexes.json for compound query coverage.
Unbounded Results:
- Any list query without LIMIT/pagination (
.findMany without take, .all() without [:limit], .result without .take).
- Flag queries that could return thousands of rows.
Sequential Queries:
- Multiple independent DB calls that could run concurrently:
- JS/TS: Sequential
await calls → Promise.all
- Python: Sequential awaits →
asyncio.gather
- Go: Sequential calls → goroutines with errgroup
- Scala: Sequential
db.run → Future.sequence / DBIO.sequence
- Ruby: Sequential queries →
Promise.all equivalent or batch loading
Transaction Scope:
- Transactions holding locks across external API calls or slow operations.
- Long-running transactions that could be broken into smaller units.
Over-fetching:
SELECT * equivalents when only a few columns are needed.
- Prisma: Missing
select clause. Django: No .values()/.only(). ActiveRecord: No .select().
- Large JSON/BLOB columns fetched unnecessarily.
Query Duplication:
- Same query executed multiple times per request (check service/handler chains).
- Queries that could be cached (e.g., config/settings loaded per request).
For each finding, estimate the impact:
- Current: ~Xms per query
- At 10x data: ~Xms per query
- Recommendation and expected improvement
============================================================
PHASE 3: API PERFORMANCE ANALYSIS
For each endpoint, trace the full call chain:
Route → Handler/Controller → Service → Repository → DB → Response
Check for:
-
Sequential I/O: Multiple independent async calls that could run in parallel.
JS: Sequential await → Promise.all. Python: sequential awaits → asyncio.gather.
Go: sequential calls → goroutines. Scala: sequential futures → Future.sequence.
-
Missing caching: Repeated identical queries across requests. Check for:
- Config/settings loaded per request instead of cached
- User session data re-fetched on every call
- Static reference data (categories, enums) queried repeatedly
-
Response payload size: Endpoints returning full objects when the client
only uses a few fields. Check frontend consumption of the endpoint.
-
Missing pagination: List endpoints without limit/offset or cursor parameters.
-
Synchronous work in request path: File processing, image resizing, email
sending, PDF generation, or other slow operations that should be backgrounded
(queued via Redis, SQS, Celery, Bull, Sidekiq, etc.).
-
External API calls without timeouts: Calls to third-party services
without explicit timeout configuration or circuit breakers.
-
Missing compression: Large JSON responses without gzip/brotli.
-
Missing connection pooling: DB connections opened per request instead of pooled.
============================================================
PHASE 4: MEMORY PROFILING
Analyze code for memory issues:
-
Memory leaks:
- Event listeners or subscriptions not cleaned up on teardown/dispose
- Closures capturing large objects unnecessarily
- Caches without eviction policies (unbounded Maps/Dicts)
- Streams or file handles not closed
- Flutter: StreamSubscription not cancelled in dispose()
- React: useEffect cleanup missing, event listeners not removed
- Node.js: Global variables growing over time, unclosed DB connections
-
Large allocations:
- Reading entire files into memory instead of streaming
- Building large arrays/lists when streaming/generators would work
- Buffering entire HTTP responses instead of streaming
- Large in-memory data structures that could be paged
-
Object retention:
- Global caches that grow without bounds
- Singleton services holding references to request-scoped data
- Circular references preventing garbage collection
============================================================
PHASE 5: BUNDLE SIZE ANALYSIS (frontend projects)
For web frontends (React, Vue, Svelte, Next.js, etc.):
-
Dependency audit: Check package.json for oversized dependencies.
Look for: moment.js (use date-fns/dayjs), lodash (use lodash-es or individual imports),
large icon libraries imported wholesale, polyfills no longer needed.
-
Code splitting: Check for lazy loading of routes/pages. Flag:
- Large single-bundle apps without route-based splitting
- Heavy components imported eagerly that could be
React.lazy / dynamic import()
- Barrel files (index.ts re-exports) that prevent tree-shaking
-
Tree-shaking effectiveness: Check for:
- CommonJS imports that block tree-shaking (require vs import)
- Side-effect imports pulling in unused code
"sideEffects": false missing in package.json
-
Asset optimization:
- Unoptimized images (missing next/image, no WebP/AVIF, no srcset)
- Fonts loaded without
font-display: swap
- CSS not purged (large Tailwind builds without purge config)
For Flutter:
- Large asset files bundled unnecessarily
- Unused packages in pubspec.yaml
- Debug-only code left in release builds
For mobile (React Native):
- Large native dependencies increasing app size
- Unused assets in the bundle
- Hermes engine not enabled (Android)
============================================================
PHASE 6: NETWORK WATERFALL ANALYSIS
Trace the network request sequence for critical user flows:
-
Request chaining / waterfalls: Sequential API calls where the second depends
on the first. Flag chains longer than 2 requests deep. Look for:
- Auth token fetch → user profile fetch → data fetch (3-deep chain)
- GraphQL queries that could be batched
- REST calls that could be combined into a single endpoint
-
Redundant requests: Same endpoint called multiple times on a single page/screen.
Common in component-based architectures where each component fetches independently.
-
Missing prefetching: Data needed on navigation that could be prefetched:
- Next page data not prefetched on hover/focus
- Critical API calls not initiated during loading states
-
Large payloads: API responses > 50KB that could be:
- Paginated, filtered server-side, or compressed
- Served from CDN/cache instead of computed per request
-
Missing HTTP caching headers: Responses that could have Cache-Control,
ETag, or Last-Modified but don't. Static/semi-static data served without caching.
-
Connection overhead: Too many unique domains requiring separate TLS handshakes.
Missing HTTP/2 or HTTP/3 multiplexing.
============================================================
PHASE 7: FRONTEND RENDERING PERFORMANCE
Flutter:
- Excessive rebuilds: StatefulWidgets with large build methods that rebuild entire subtrees
- Providers/BLoCs that trigger too many rebuilds (check selector granularity)
- Missing const constructors on static widgets
- ListView without builder pattern for large lists
- Missing keys on dynamic lists causing unnecessary rebuilds
- Large images loaded without caching or size constraints
- Heavy computation on the main isolate (should use compute/isolates)
React / Next.js / Vue:
- Missing memoization: Components re-rendering without React.memo / useMemo / computed
- Render cascades: State changes causing unnecessary re-renders down the tree
- Large component trees without virtualization (use react-window/react-virtualized)
- Expensive calculations in render path without memoization
- Layout thrashing: Reading DOM layout then immediately writing (forced reflows)
Svelte / SvelteKit:
- Reactive statements triggering unnecessary updates
- Large lists without virtual scrolling
============================================================
PHASE 8: OPTIMIZATION RECOMMENDATIONS
Rank all findings by estimated impact:
- CRITICAL (>50% latency reduction): N+1 queries on hot paths, unbounded queries,
sequential I/O that could be parallel, memory leaks causing degradation over time.
- HIGH (20-50% reduction): Missing indexes, over-fetching, missing caching,
large bundle sizes blocking initial load, deep network waterfalls.
- MEDIUM (5-20% reduction): Response payload optimization, widget rebuild reduction,
missing code splitting, redundant network requests.
- LOW (<5% reduction): Micro-optimizations, minor cleanup, marginal bundle savings.
============================================================
SELF-HEALING VALIDATION (max 3 iterations)
After completing fixes, re-validate your work:
- Re-run the specific checks that originally found issues.
- Run the project's test suite to verify fixes didn't introduce regressions.
- Run build/compile to confirm no breakage.
- If new issues surfaced from fixes, add them to the fix queue.
- Repeat the fix-validate cycle up to 3 iterations total.
STOP when:
- Zero Critical/High issues remain
- Build and tests pass
- No new issues introduced by fixes
IF STILL FAILING after 3 iterations:
- Document remaining issues with full context
- Classify as requiring manual intervention or architectural changes
============================================================
OUTPUT
Performance Profile
Stack: {detected stack}
Scope: {what was profiled}
Database Queries
| Query Pattern | Location | Issue | Current Est. | At 10x | Fix |
|---|
| {pattern} | {file:line} | {issue} | ~{X}ms | ~{X}ms | {recommendation} |
API Endpoints
| Endpoint | Bottleneck | Current Pattern | Recommended | Est. Improvement |
|---|
| {path} | {bottleneck} | {current} | {recommended} | ~{X}% faster |
Memory Issues (if found)
| Location | Issue | Severity | Fix |
|---|
| {file:line} | {issue} | {severity} | {fix} |
Bundle Size (if frontend)
| Item | Size | Issue | Recommendation | Savings |
|---|
| {dep/chunk} | {size} | {issue} | {recommendation} | ~{X}KB |
Network Waterfall (if applicable)
| Flow | Chain Depth | Total RTT Est. | Issue | Fix |
|---|
| {user flow} | {depth} | ~{X}ms | {issue} | {fix} |
Frontend Rendering (if applicable)
| Component | Issue | Impact | Fix |
|---|
| {component} | {issue} | {impact} | {fix} |
Top 5 Optimizations (ranked by impact)
-
{title} — {description}
- Location:
{file:line}
- Estimated improvement: ~{X}% latency reduction
- Effort: {S/M/L}
-
...
Summary
- Hottest path: {most performance-sensitive code path}
- Biggest win: {highest impact, lowest effort optimization}
- Estimated overall improvement: ~{X}% if top 5 fixes applied
NEXT STEPS:
- "Run
/iterate to implement the top optimizations."
- "Run
/scale-audit for a broader scalability assessment."
- "Run
/e2e after optimizations to verify nothing broke."
============================================================
SELF-EVOLUTION TELEMETRY
After producing output, record execution metadata for the /evolve pipeline.
Check if a project memory directory exists:
- Look for the project path in
~/.claude/projects/
- If found, append to
skill-telemetry.md in that memory directory
Entry format:
### /perf — {{YYYY-MM-DD}}
- Outcome: {{SUCCESS | PARTIAL | FAILED}}
- Self-healed: {{yes — what was healed | no}}
- Iterations used: {{N}} / {{N max}}
- Bottleneck: {{phase that struggled or "none"}}
- Suggestion: {{one-line improvement idea for /evolve, or "none"}}
Only log if the memory directory exists. Skip silently if not found.
Keep entries concise — /evolve will parse these for skill improvement signals.