To optimize code performance with AI, measure before you prompt: profile the slow path, give the agent the profile, ask for the smallest change that removes the hotspot, and accept it only when a benchmark shows the difference. Coding agents such as Claude Code, Codex and Gemini CLI are fast at reading a flame graph, tracing a hot function back to its callers and rewriting a query. Left without data, they do what anyone does: guess. And guessed optimizations are the expensive kind, because they make code harder to read without making it faster.
This guide covers a workflow that holds up in practice: define the goal, profile, hand over the evidence, change one thing, verify with a benchmark and lock the win in with a regression guard. Along the way it covers the problems that cause most slowdowns in real apps: N+1 queries, bundle size and missing caches.
Start with a number, not a feeling
“The dashboard is slow” is not a task. Turn it into something an agent can check:
- What is slow: one endpoint, one page load, one CLI command, one batch job.
- How slow it is now, measured: p95 latency of an endpoint, time to interactive of a page, run time of a script.
- What “fast enough” means: a target, such as “under 300 ms at p95” or “the import finishes in under a minute for 100k rows”.
- Under which conditions: data size, cold or warm cache, production build or dev server.
That last point catches a lot of wasted work. A React app on the dev server, a Node service without NODE_ENV=production, or a Python job on ten rows of test data will show hotspots that don’t exist in production. Measure the thing you ship.
Step 1: profile the slow path
Pick the profiler that matches where the time goes.
In the browser
The Performance panel in Chrome DevTools records a trace: long tasks, scripting, layout, network waterfalls. Lighthouse gives a lab score with concrete opportunities. The Lighthouse CLI is handy because it writes a file you can hand to an agent:
npm install -g lighthouse
lighthouse https://localhost:3000/dashboard \
--only-categories=performance \
--output=json --output-path=./lighthouse.json
If your agent supports MCP, the Chrome DevTools MCP server lets it start a performance trace and read the insights itself:
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest"]
}
}
}
In Node.js
Node has a built-in sampling profiler. --cpu-prof writes a .cpuprofile file when the process exits, which you can open in the DevTools Performance panel or give to an agent:
node --cpu-prof --cpu-prof-dir=./profiles server.js
# run the slow request a few times, then stop the server
The older --prof flag writes a V8 log that node --prof-process turns into a text report, which is very easy for an agent to read:
NODE_ENV=production node --prof server.js
node --prof-process isolate-0x*-v8.log > processed.txt
For memory problems, --heap-prof writes a .heapprofile in the same way.
In Python
py-spy samples a running process without changing your code, which makes it the safe choice for a live service:
py-spy top --pid 12345 # live view, like top
py-spy record -o profile.svg --pid 12345 # flame graph
py-spy record -o profile.svg -- python import_job.py
For a script you can start yourself, the standard library’s cProfile gives exact call counts:
python -m cProfile -s cumtime import_job.py | head -40
As of October 2026, Python 3.15 also ships a profiling package with a statistical profiler (python -m profiling.sampling) and keeps cProfile as an alias of its tracing profiler; on earlier versions, cProfile and py-spy are the tools to use.
In the database
For query time, the database already knows the answer: the slow query log, pg_stat_statements on PostgreSQL, and EXPLAIN ANALYZE for one query. Write SQL with AI covers reading query plans and choosing indexes with an agent, so this guide won’t repeat it.
Step 2: give the agent the evidence
Now the agent has something real to work with. Hand over the profile and the context, and ask for analysis before code:
The /api/orders endpoint takes about 1.8 s at p95 in production; the target
is 300 ms. Attached: processed.txt from node --prof-process for 50 requests,
and the slow query log for the same period.
Don't change code yet. List the top three hotspots, with the evidence from
the profile for each one (function, self time or total time, call count),
the likely cause in our code (file and line), and a proposed fix. Order
them by expected impact.
A few habits make this step work:
- Give it the raw output, not your summary. Your summary already contains your guess.
- Ask for evidence per claim. “This function is slow” should come with a number from the profile.
- Keep the agent in the hot path. Code that takes 2% of the time is not worth a rewrite, however ugly it is.
If the agent says something like “the profile doesn’t show the bottleneck clearly”, believe it, and capture a better profile with more load or the production build. That’s a much better result than a confident guess.
Step 3: write the benchmark before the fix
The benchmark is your verification step. Write it, run it on the current code, and save the baseline before anything changes.
For a command or script, hyperfine runs it many times and reports the mean and spread:
hyperfine --warmup 3 'node scripts/build-report.js'
For a function, use the benchmark support of your stack: go test -bench=. -benchmem in Go, pytest-benchmark in Python, the bench support in your JavaScript test runner. For an endpoint, a short load test with a fixed number of requests against a production build.
Then tell the agent the rule:
Here's the benchmark and its baseline result. Make one change at a time.
After each change, run the tests and the benchmark three times and report
before/after. If a change doesn't improve the result beyond the noise,
revert it and tell me, even if you think it should help.
Two details matter. Compare on the same machine with the same data, because laptops throttle and caches warm up. And keep tests running alongside the benchmark: a faster function that returns the wrong answer is a bug. If the code you’re optimizing has no tests, add them first; write unit tests with AI shows how to get useful ones from an agent.
Where the time usually goes
In real apps, a small set of problems accounts for most slowdowns. These are the ones to check first.
N+1 queries
The classic: load a list, then run one extra query per item inside a loop or a template. It looks fine with ten rows in development and falls over with two thousand in production. The fix is to load the related data in one or two queries, with the tool your ORM provides:
| Stack | Eager loading |
|---|---|
| Django | select_related (joins), prefetch_related (separate query) |
| Rails | includes, and the Bullet gem to detect N+1 in development |
| SQLAlchemy | selectinload, joinedload |
| Prisma | include |
Agents are good at this fix once they know where it happens. Turn on query logging, open the slow page once, and give the agent the log: a repeated query pattern with different IDs is the fingerprint. Then ask for the fix and for a test that asserts the number of queries on that page, so the problem can’t come back unnoticed.
Bundle size
For front-end performance, the JavaScript you ship is often the biggest lever. Measure what’s in the bundle first with source-map-explorer or webpack-bundle-analyzer (or the analyzer for your bundler), then look for the usual causes:
- a large library imported for one function,
- a whole icon set or locale bundle pulled in by one import,
- heavy components loaded on first paint that could be lazy-loaded,
- the same dependency bundled twice in different versions.
Give the agent the analyzer output and ask for changes ranked by bytes saved. Then compare the build output before and after, the same way you compare a benchmark.
Repeated work and caching
A cache is the right answer when the same expensive result is computed again and again with the same inputs. It is the wrong answer when the computation could just be made cheap, or when nobody has decided what happens when the data changes. Before you accept a cache from an agent, ask it:
For this cache: what's the key, what invalidates it, what's the maximum
staleness we accept, how much memory can it use, and what happens on a
cold start? Show me the measured hit rate or the profile that justifies it.
If the answers are vague, the cache will turn into a bug report later. Often the better fix is earlier in the chain: a missing index, a query that fetches every column, a loop that recomputes the same value on each iteration.
Algorithmic problems
Sometimes the profile shows a single function with a huge call count: a nested loop over two lists, a .find() inside a .map(), a sort inside a loop. Replacing a linear lookup with a Map or Set is a classic agent fix, and the benchmark tells you immediately whether it mattered.
Avoid premature and speculative optimization
Agents love to optimize. Ask one to “improve performance” on a file and it will memoize components, rewrite loops and add caches everywhere. Most of those changes move nothing measurable and make the code harder to change. Rules worth stating plainly in your project instructions:
## Performance work
- No optimization without a profile or benchmark that shows the problem.
- One change per commit, with before/after numbers in the message.
- Readability wins over micro-optimizations outside measured hot paths.
- Don't add caches, workers or new dependencies without asking.
The same applies to the agent’s own suggestions during unrelated work. If it proposes “while I’m here, I also optimized…”, ask for the numbers or ask it to revert. When something does break after an optimization, debug code with AI covers how to narrow it down without guessing.
Step 4: guard the win
Performance degrades quietly, one reasonable change at a time. Turn each fix into a check that fails loudly:
- Query count tests. Assert that the orders page runs at most a fixed number of queries. Most frameworks have a helper for this, such as
assertNumQueriesin Django. - Bundle budgets. Tools like
size-limitfail the build when a bundle grows beyond a set size. - Lighthouse in CI. Run the Lighthouse CLI against a preview build and fail below a score threshold on the pages that matter.
- Benchmarks against a baseline. Store the baseline and compare; in Go,
benchstatcompares two benchmark runs and tells you whether the difference is significant.
Agents are good at writing these guards, and it’s worth asking for one in the same task as the fix: “fix the N+1 and add a test that would have caught it”.
Doing it in AgentVera
AgentVera is a desktop app that runs coding-agent CLIs like Claude Code, Codex and Gemini CLI side by side. Several of its features fit this loop:
- A browser the agent can drive. The agent browser is a separate Chrome profile that agents use through Playwright MCP, with DevTools and the console built in, so the agent can open the slow page itself and you can watch it.
- Send evidence in one shortcut. Send to agent (⌘⇧L) sends terminal output, an editor selection, a diff or the browser console to an agent. Profiler output in the terminal goes to the agent without copy and paste; very long output is cut at 12,000 characters, so send the top of the report or save it to a file.
- Query plans next to the code. The database manager (Pro) opens PostgreSQL, MySQL and SQLite with an SQL editor that shows EXPLAIN plans, and agents can query the database through its built-in tool.
- Benchmarks as acceptance criteria. Write “the tests pass and the benchmark is at least 30% faster than the baseline” in your message, and Verify checks it at the end of the turn with a verifier that runs commands but can’t edit files.
- Undo an optimization that didn’t pay off. Checkpoints snapshot each turn, so reverting an experiment is one click.
- Try two approaches at once. In the workspace grid, run two agents in separate worktrees, say one on caching and one on the query, and keep the one with the better numbers.
Wrapping up
Optimizing code performance with AI is the same discipline as optimizing without it, just faster: a clear target, a profile, the evidence handed over, one change at a time, a benchmark as proof and a guard to keep the win. The agent does the reading and the rewriting; the numbers decide. If your slowdown came with a dependency upgrade, the workflow in upgrade dependencies with AI helps you isolate which package caused it.
To try this workflow on your own project, download AgentVera.