Skip to content
Skill · Claude CodeSKILL.mdCoding

Profile Before Optimizing

A measurement-first optimization loop — profile under realistic load, fix exactly one hot spot, re-measure, and keep the numbers in the commit.

Use when: Use when something is slow or resource-hungry and the urge is to start rewriting code that merely looks expensive.

@libraryNewcomer
0 upvotes0 forks0 comments

The skill file

Claude Code · SKILL.md
SKILL.md3164 chars
# Profile Before Optimizing Optimizations without measurements are guesses, and most guesses about hot paths are wrong. No code change until a profile names the culprit; no merged change without a before/after number. ## Step 1 — Define the metric and budget - Pick one number: p95 request latency, wall-clock of a batch job, peak RSS, queries per request. "Feels slow" is not a metric. - Record the current value and the target ("p95 from 1.8s to <500ms"). The target tells you when to stop — open-ended optimization never ends. ## Step 2 — Measure under realistic conditions - Profile with production-like data volume. Code that's fine at 100 rows is quadratic at 100k; a dev DB with 50 records hides every N+1. - Use the right tool for the layer: - CPU: a sampling profiler with flame graph output (`py-spy`, `node --cpu-prof` + speedscope, `perf`, pprof). - Database: query logs with timings, `EXPLAIN ANALYZE` on the slow queries, a counter for queries-per-request. - Memory: heap snapshots diffed across the operation; allocation profilers for churn. - Frontend: browser performance panel, Web Vitals — measured on a throttled profile, not your dev machine. - Run the measurement at least 3 times after a warm-up run; record median and spread. A single timing is noise. ## Step 3 — Read the profile, name one culprit - Walk the flame graph top-down by inclusive time. Ignore anything under ~5% of total — it cannot give you a meaningful win even if reduced to zero. - Distinguish on-CPU from waiting. If the process is idle while the wall clock runs, the fix is I/O-shaped (batching, caching, parallelism, indexes), not algorithmic. - Classic culprits to check before exotic theories: N+1 queries, missing index (seq scan in EXPLAIN), serialization of large payloads, synchronous calls in a loop that could be batched, accidental O(n²) via `includes`/`in` inside a loop, re-reading or re-parsing the same file/config per item. - Write the diagnosis down: "62% of wall time is 1,400 sequential SELECTs from rendering badges per row." ## Step 4 — Fix one thing - Apply the single change that addresses the named culprit. Prefer, in order: do less work (cache, skip, precompute) → do it in bulk (batch query, vectorize) → do it concurrently → micro-optimize. Micro-optimizations come last because they cost readability for single-digit percents. - Do not bundle a second "while I'm in here" optimization. One change per measurement, or you can't attribute the result. ## Step 5 — Re-measure and decide - Re-run the exact Step 2 measurement. Compare medians. - Improvement below the noise floor → revert. Keep no complexity that isn't paid for by a measured win. - Target met → stop, even if you can see more opportunities. File them instead. - Target not met → back to Step 3 with a fresh profile; the hot path has moved. ## Commit etiquette - The commit/PR body includes: metric, before, after, measurement method, and a link to or summary of the profile. "perf: batch badge query (p95 1.8s → 410ms, 1400 queries → 3)". - Add a regression guard where feasible: a query-count assertion in a test, or a benchmark in CI for the critical path.

Install

drop into your repo

Save this to your project or home directory so Claude Code can load it.

path~/.claude/skills/performance-profiling-before-optimizing/SKILL.md
Discussion

What people are saying

Forks

0 forks of this skill
Loading forks…