Claude Code Performance Optimization: From Measurement to Core Web Vitals
Measure and improve LCP, INP, API latency, bundles, and caching with Claude Code and runnable examples.
Performance optimization is not a vague attempt to make a web app feel faster. It is the discipline of measuring what users wait for, what blocks interaction, what shifts on screen, and what wastes server resources, then reducing those costs with evidence.
Claude Code is useful here because it can inspect code, trace likely bottlenecks, propose focused patches, and help write verification steps. The important part is the workflow: measure first, form a hypothesis, make a small change, and measure again. If you only ask Claude Code to “make this fast,” you will often get plausible but unproven changes.
Use the official references as your baseline: web.dev Core Web Vitals, Lighthouse docs, and the MDN Performance API.
Define Fast Before Editing
Web performance has several dimensions. LCP, or Largest Contentful Paint, tracks when the main visible content appears. INP, or Interaction to Next Paint, tracks how heavy interactions feel after a click, tap, or key press. CLS, or Cumulative Layout Shift, tracks unexpected movement while the page loads.
For application code, you also need API p75 latency, database query count, JavaScript bundle size, image transfer size, and CPU-heavy work in the browser. Average latency hides unhappy users, so p75 is often more practical: it shows the slower end of normal traffic without being dominated by a single outlier.
flowchart LR
Measure["Measure"] --> Hypothesis["Form a hypothesis"]
Hypothesis --> Patch["Patch one thing"]
Patch --> Verify["Measure again"]
Verify --> Keep["Keep only proven wins"]
| Metric | Meaning | First target |
|---|---|---|
| LCP | Main content appears | Aim for 2.5s or less |
| INP | Interaction responsiveness | Aim for 200ms or less |
| CLS | Unexpected layout movement | Aim for 0.1 or less |
| API p75 | Latency most users feel | Define per screen |
| JS transfer | JavaScript loaded up front | Compare per route |
Give Claude Code Better Inputs
The best prompt includes the symptom, reproduction steps, current numbers, and constraints.
The /products page in this repository is slow.
The goal is to reduce mobile LCP and API p75 latency.
Current measurements:
- Lighthouse mobile Performance: 58
- LCP: 4.2s
- INP: 180ms
- CLS: 0.04
- /api/products p75: 920ms
Please:
1. Inspect images, bundle size, API code, and database access.
2. Do not change code based on guesses only.
3. Prefer low-risk patches with measurable impact.
4. Include the commands I should run after the change.
For related local reading, see the Complete Debugging Guide, How to Supercharge React Development, and the Claude Code Image Optimization Guide.
Runnable Measurement Script
This Node.js 18+ script measures a page or API endpoint and prints average and p75 latency.
// measure-url.mjs
import { performance } from "node:perf_hooks";
const url = process.argv[2] ?? "https://example.com/";
const runs = Number(process.argv[3] ?? 5);
async function measureOnce() {
const start = performance.now();
const res = await fetch(url, { cache: "no-store" });
await res.arrayBuffer();
return {
status: res.status,
ms: performance.now() - start,
};
}
const samples = [];
for (let i = 0; i < runs; i += 1) {
samples.push(await measureOnce());
}
samples.sort((a, b) => a.ms - b.ms);
const avg = samples.reduce((sum, s) => sum + s.ms, 0) / samples.length;
const p75Index = Math.floor((samples.length - 1) * 0.75);
const p75 = samples[p75Index].ms;
console.table(
samples.map((sample, index) => ({
run: index + 1,
status: sample.status,
ms: sample.ms.toFixed(1),
}))
);
console.log(`avg=${avg.toFixed(1)}ms p75=${p75.toFixed(1)}ms`);
node measure-url.mjs http://localhost:3000/api/products 7
Give this output to Claude Code before and after the patch. It turns performance work from opinion into a repeatable comparison.
Realistic Use Cases
| Use case | Common cause | What to ask Claude Code |
|---|---|---|
| SaaS dashboard loads slowly | Every widget fetches on first paint; chart bundle is huge | Defer non-critical widgets and split the chart code |
| Ecommerce listing is slow | Oversized images and review/inventory N+1 queries | Fix image sizes and database include/select usage |
| Media article has poor LCP | Hero image loads late; ad tags run too early | Prioritize the main image and delay third-party scripts |
| Admin search freezes | Large array filtering on the main thread | Move heavy work, reduce complexity, or paginate |
N+1 means the app fetches a list once, then runs another query for each row. It can look fine with 5 records and fail badly with 100. Caching means reusing a result for a short period, but user-specific or permission-sensitive data must not be placed into a shared cache.
Runnable Cache Example
This Express example compares a deliberately slow endpoint with a cached endpoint. Production systems often use Redis or a CDN, but an in-memory demo makes the behavior easy to see.
npm init -y
npm install express
node cached-api.mjs
// cached-api.mjs
import express from "express";
import { performance } from "node:perf_hooks";
const app = express();
const cache = new Map();
const ttlMs = 30_000;
async function loadProducts() {
await new Promise((resolve) => setTimeout(resolve, 800));
return [
{ id: 1, name: "Starter Plan", price: 1200 },
{ id: 2, name: "Pro Plan", price: 4800 },
];
}
async function cached(key, loader) {
const now = Date.now();
const hit = cache.get(key);
if (hit && hit.expiresAt > now) return { data: hit.data, cache: "hit" };
const data = await loader();
cache.set(key, { data, expiresAt: now + ttlMs });
return { data, cache: "miss" };
}
app.get("/api/products/raw", async (_req, res) => {
const start = performance.now();
const data = await loadProducts();
res.json({ cache: "none", ms: performance.now() - start, data });
});
app.get("/api/products/cached", async (_req, res) => {
const start = performance.now();
const result = await cached("products", loadProducts);
res.json({ ...result, ms: performance.now() - start });
});
app.listen(3000, () => {
console.log("Open http://localhost:3000/api/products/raw");
});
Measure it from another terminal:
node measure-url.mjs http://localhost:3000/api/products/raw 3
node measure-url.mjs http://localhost:3000/api/products/cached 3
When asking Claude Code to add caching, specify the scope: “cache popular products for 30 seconds,” “do not cache inventory,” or “include the user ID in the cache key.”
Runnable Algorithm Example
Network is not always the bottleneck. Expensive synchronous JavaScript can hurt INP. This example replaces repeated includes scans with a Set.
// compare-lookup.mjs
import { performance } from "node:perf_hooks";
const a = Array.from({ length: 40_000 }, (_, i) => i);
const b = Array.from({ length: 40_000 }, (_, i) => i * 2);
function slowIntersection(left, right) {
return left.filter((item) => right.includes(item));
}
function fastIntersection(left, right) {
const rightSet = new Set(right);
return left.filter((item) => rightSet.has(item));
}
function time(label, fn) {
const start = performance.now();
const result = fn();
const ms = performance.now() - start;
console.log(`${label}: ${ms.toFixed(1)}ms (${result.length} hits)`);
}
time("slow", () => slowIntersection(a, b));
time("fast", () => fastIntersection(a, b));
node compare-lookup.mjs
Common Failure Modes
Do not optimize only for a Lighthouse score. Lighthouse is lab data, while real users have different devices, networks, extensions, and cache states. Pair it with Search Console, real-user monitoring, and server logs.
Do not add cache without correctness rules. Prices, inventory, permissions, and personal data need clear cache keys, TTLs, and invalidation.
Do not add useMemo everywhere. React memoization should protect measured hot paths; otherwise it adds complexity and dependency-array bugs.
Do not prioritize every image. Above-the-fold images need stable dimensions and sometimes priority loading, but below-the-fold images should usually stay lazy.
Do not split bundles blindly. Use the ideas in Claude Code Bundle Analysis and Claude Code Code Splitting to compare route-level impact.
Add Rules to CLAUDE.md
## Performance rules
- Before optimizing, record the current metric and target metric.
- Prefer small, measurable changes over broad rewrites.
- Do not introduce shared cache for user-specific data.
- Avoid N+1 queries; use select/include or batching.
- Keep above-the-fold images sized and stable.
- After changes, report commands used for verification.
These rules keep Claude Code focused on measurable work instead of cosmetic changes.
Consulting CTA
ClaudeCodeLab can help audit existing web apps with Claude Code, improve Core Web Vitals, isolate API and database latency, and produce a practical performance runbook. For a useful first review, prepare the target URL, key screens, Lighthouse output, slow API logs, and the deadline for improvement.
Hands-On Result
Running the sample locally, the intentionally delayed /api/products/raw endpoint stays around the 800ms range, while /api/products/cached drops to a few milliseconds after the first request. The compare-lookup.mjs script also shows a large reduction when using Set. Real projects are messier, but the pattern holds: measure p75 before, change one thing, and measure again with the same tool.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Permission Receipt Pattern: Record Scope, Proof, and Rollback
A permission receipt pattern for Claude Code: allowed actions, approval boundaries, proof commands, rollback, and revenue CTA checks.
Safe Agent Harness Design for Claude Code and Codex: Permissions, Checks, and Rollback
Build a practical agent harness for Claude Code and Codex with policy, planning, verification, and recovery layers.
Claude Code Subagents: A Practical Guide to Safe Agent Delegation
Claude Code subagent guide for safe parallel article and code work: delegation rules, prompts, pitfalls, and checks.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.