perf

Performance profiler — analyzes DB queries (any ORM), API call chains, memory usage, bundle sizes, network waterfalls, and frontend rendering. Produces ranked optimization recommendations with estimat

SKILL.md

GitHub

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

  1. 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
  2. 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)
  3. 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
  4. 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:

  1. Re-run the specific checks that originally found issues.
  2. Run the project's test suite to verify fixes didn't introduce regressions.
  3. Run build/compile to confirm no breakage.
  4. If new issues surfaced from fixes, add them to the fix queue.
  5. 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)

  1. {title} — {description}

    • Location: {file:line}
    • Estimated improvement: ~{X}% latency reduction
    • Effort: {S/M/L}
  2. ...

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.