agentleFS
ak-shipak-ship/fullstack-agent-skills/skills/perf-hunter/SKILL.md

perf-hunter

Skill0 starsChanged 6 months agoMITView on GitHub

Find and fix the actual bottleneck — not the one you assume. Profiles before optimizing, measures before and after, refuses to "rewrite for perf" without numbers. Covers React/Vue rendering, bundle size, database query plans, N+1 patterns, memory leaks, and Node.js event-loop lag. Use when the user says "make this faster", "why is this slow", "optimize this", "the page is laggy", "the query is slow", or "this endpoint times out".

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 6 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Do *not* trigger for: code style optimizations that don't have measured impact (use refactor-master), perceived perf issues without numbers …

    line 18
  2. Distinctive:

    If the user can't answer those, ask.

    line 43
  3. Distinctive:

    If the after number didn't move, revert.

    line 110
  4. Distinctive:

    Add a regression guard:

    line 114
  5. Distinctive:

    Don't add a cache without an invalidation plan.

    line 131
  6. Very specific:

    Don't useMemo everything in a React component.

    line 132
  7. Distinctive:

    Don't switch from REST to GraphQL "for perf".

    line 133
  8. Very specific:

    Don't rewrite in Rust/Go/etc.

    line 134
  9. Distinctive:

    Don't optimize what runs once.

    line 135

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

perf-hunter — measure, fix, measure again

When to use this skill

Trigger when something is slow and someone wants it faster. Strong signals:

  • "why is this slow?", "make this faster", "this is too slow"
  • "the page takes 5 seconds to load"
  • "the query times out"
  • "we're hitting our function memory limit"
  • A flame graph, profiler output, or DevTools screenshot pasted in

Do not trigger for: code style optimizations that don't have measured impact (use refactor-master), perceived perf issues without numbers ("it feels slow") — get numbers first.

The output contract

A perf change that:

  1. Has a before number — measured under realistic conditions, not synthetic best-case.
  2. Targets the actual bottleneck — identified by profiling, not guessing.
  3. Has an after number — same measurement, same conditions.
  4. Quantifies the trade-off — what got harder to maintain, what runtime cost moved where.
  5. Includes a regression test — a benchmark or a budget that fails if the perf regresses.

Without those four numbers and a regression test, it's not a perf fix — it's vibes.

Workflow

1 — Define "slow"

Get specific:

  • What operation? (page load, API call, query, render, build)
  • How slow? (p50, p95, p99 — averages lie)
  • Compared to what? (target, prior version, competitor)
  • Under what load? (1 user vs 1000, cache cold vs warm, payload size)

If the user can't answer those, ask. Optimizing the wrong p50 doesn't move the p95 the users notice.

2 — Measure before

Pick the right tool for the surface:

Frontend:

  • Chrome DevTools Performance panel — record a real interaction, find the long tasks
  • Lighthouse for page-load metrics (LCP, INP, CLS, TBT)
  • React Profiler for component render times
  • webpack-bundle-analyzer / rollup-plugin-visualizer / source-map-explorer for bundle size

Backend Node:

  • --prof for CPU profiling, then node --prof-process → flame chart with 0x or clinic flame
  • clinic doctor for high-level (event-loop lag, GC, CPU, memory)
  • clinic bubbleprof for async I/O patterns
  • For HTTP: autocannon or k6 for load + percentiles

Database:

  • EXPLAIN ANALYZE for the slow query (Postgres) — read the actual plan
  • pg_stat_statements extension to find the queries burning the most time across the workload
  • EXPLAIN FORMAT=JSON (MySQL), db.collection.explain('executionStats') (Mongo)

Record numbers. Don't proceed without them.

3 — Diagnose

Common patterns and the smell they leave:

N+1 queries — code makes one query, then loops and queries per result. Smell: many identical queries in the log, total query count ~= result count.

  • Fix: IN (...) batch, JOIN, or a DataLoader-style batcher.

Sequential awaits when parallel works — for (const id of ids) { await fetch(id) } is N round-trips. Smell: latency scales with N.

  • Fix: await Promise.all(ids.map(fetch)). Cap concurrency with p-limit if needed.

Re-renders in React — child renders even when its props haven't changed. Smell: React Profiler shows wide flame.

  • Fix: lift state, useMemo for derived values, React.memo for components, stable references for callbacks. Don't sprinkle these — find the actual culprit first.

Bundle bloat — initial JS payload is 800kB+. Smell: Lighthouse LCP poor, FCP fine.

  • Fix: dynamic import() for non-critical routes, tree-shake (look for import * as X), check for whole libraries imported for one function (date-fns sub-imports, lodash-es).

Missing indexes — sequential scan on a 10M-row table. Smell: EXPLAIN ANALYZE shows Seq Scan + high actual time.

  • Fix: targeted index. Validate with EXPLAIN ANALYZE that the plan changed.

Bad index usage — index exists but planner skips it. Smell: EXPLAIN shows Filter after the scan rather than Index Cond.

  • Fix: usually a type mismatch in the query (WHERE id = '123' against integer column), or stale statistics (ANALYZE).

Memory leak — RSS climbs over hours/days, never plateaus. Smell: process restarts every N days "for hygiene".

  • Fix: heap snapshots before/after a workload; look for unbounded caches, retained closures, untracked subscriptions, growing Maps indexed by request ID.

Event-loop lag — Node app's response times balloon under load even though the work is async. Smell: clinic doctor shows lag > 50ms.

  • Fix: usually a sync CPU-heavy step (JSON.parse of a huge body, regex, crypto on the request thread) — move to a worker_thread or off the request path.

4 — Fix the one thing

Apply the smallest change that addresses the diagnosis. Don't pre-optimize the next thing you "noticed while you were there".

5 — Measure after

Same tool, same scenario, same load. Record the new number. Report the delta in absolute and relative terms:

Before: p95 endpoint latency 1240ms
After:  p95 endpoint latency 180ms  (-85%)
Trade-off: added an in-memory index that uses ~40MB heap per worker.

If the after number didn't move, revert. The fix wasn't the right one.

6 — Lock the win

Add a regression guard:

  • Backend: a perf test in CI (autocannon or k6 with a p95 threshold)
  • Frontend: bundle-size budget (@bundle-analyzer/cli, Next.js experimental.bundlePagesRouterDependencies, or bundlewatch)
  • DB: a query that the test suite runs EXPLAIN ANALYZE on, asserting the plan uses the new index

Without a guard, the fix decays. The next refactor will un-do it silently.

Patterns and anti-patterns

✅ Do:

  • Optimize the hot path. 80% of latency typically lives in 20% of the code. Profile.
  • Cache at the right layer — closest to the consumer that can tolerate the staleness. Browser, CDN, app-memory, Redis, DB materialized view.
  • For DB perf: read the plan. The plan tells the truth; intuition lies.
  • Save a copy of the profile/flame chart in the PR — future you will need to know what the world looked like before.

❌ Don't:

  • Don't add a cache without an invalidation plan. The bugs you create with a stale cache are worse than the latency you save.
  • Don't useMemo everything in a React component. Memoization itself costs CPU and memory. Profile first.
  • Don't switch from REST to GraphQL "for perf". The wire format isn't usually the bottleneck.
  • Don't rewrite in Rust/Go/etc. before you've fixed the obvious things in the current stack.
  • Don't optimize what runs once. A 10x speedup on a script that runs at deploy time is worth nothing.

Example invocation

User: "Our /api/dashboard endpoint takes 4 seconds. Make it fast."

  1. Get numbers: p95 is 4.2s, p99 is 7s, cache cold. Target: p95 < 500ms.
  2. Profile: turn on Postgres log_min_duration_statement = 100. Make one request, check the log.
  3. Diagnose: 47 queries logged. Looks like N+1 — for each org member, the code fetches their last 5 activities individually.
  4. Fix: replace with one batched query joining users + activities + filtering for "last 5 per user" via a window function. Single query, ~20ms.
  5. Measure after: p95 down to 180ms. Trade-off: query is longer to read, added a comment explaining the window function.
  6. Lock: add a EXPLAIN ANALYZE assertion in the integration test that the plan uses the (user_id, created_at DESC) index.
  7. Report: -96% latency, single query replaces 47, regression test in place.

See also

  • code-auditor — to find the N+1 patterns and unbounded loops before they're slow
  • schema-architect — when the fix is an index that the schema is missing
  • refactor-master — when the perf fix is structural (e.g., pulling I/O out of a hot loop)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.