Analiza wydajności aplikacji z Claude Code
ENAnaliza wydajności aplikacji z Claude Code to pętla „najpierw pomiar”, prowadzona z terminala: agent zapisuje powtarzalny baseline, uruchamia prawdziwy profiler albo EXPLAIN ANALYZE, szereguje wąskie gardła z dowodami i poprawia jedno naraz. O tym, czy zmiana przyspieszyła system, rozstrzyga budżet wydajności spisany przed poprawką, a nie relacja agenta.
Ta strona jest dla programisty odpowiedzialnego za serwis, który zwolnił, i dla tech leada, który chce poprawek wydajności z dowodem w komplecie. W poprzednim kwartale API zamówień odpowiadało w około 120 ms, a teraz potrzebuje prawie dwóch sekund. Żaden pojedynczy commit tego nie tłumaczy, a dodatkowe serwery nie pomogły. Poproś agenta, żeby „przyspieszył to”, a przepisze kod, który wygląda na wolny. To rzadko jest kod, który naprawdę jest wolny.
Co znajdziesz w tym przewodniku
Dział zatytułowany „Co znajdziesz w tym przewodniku”- Konfigurację uprawnień, w której Claude Code uruchamia profilery i benchmarki, ale nie może edytować benchmarku, którym jest oceniany
- Plik z baseline’em i budżetem, który ustala próg zaliczenia, zanim zmieni się jakikolwiek kod
- Polecenia profilujące dla Node.js, Pythona i PostgreSQL oraz prompt, który zamienia ich wynik w uszeregowaną listę hotspotów ze wskazaniem źródła
- Pięć promptów do skopiowania: baseline, profil, poprawka zapytania, szukanie wycieku, poprawka z dowodem
- Tabelę dowodów, które potwierdzają przyspieszenie bez czytania całego diffa
- Typowe awarie, przez które optymalizacja prowadzona przez agenta daje fałszywy wynik, i sposób wyjścia z każdej z nich
Dlaczego agent potrzebuje baseline’u, zanim zacznie optymalizować?
Dział zatytułowany „Dlaczego agent potrzebuje baseline’u, zanim zacznie optymalizować?”Model czytający kod widzi wzorce: zagnieżdżoną pętlę, SELECT *, wywołanie synchroniczne. Nie widzi, które z nich wykonują się dziesięć tysięcy razy na żądanie, a które raz przy starcie. Statyczny przegląd daje wiarygodnie brzmiącą listę „optymalizacji”, która często mija się z prawdziwym kosztem, a każda z nich dokłada diff, któremu musisz zaufać.
Profiler i plan zapytania zastępują zgadywanie pomiarem. Dlatego zasada na tej stronie brzmi: żadnej poprawki bez zmierzonego baseline’u, profilu wskazującego hotspot i progu zaliczenia spisanego przed zmianą. Claude Code dobrze radzi sobie z żmudną częścią tej pętli: pisze harness, uruchamia go wiele razy, czyta profil na 40 000 linii i porównuje liczby. Tobie zostaje jedna decyzja, której agent nie powinien podejmować sam: co znaczy „wystarczająco szybko”.
Skonfiguruj Claude Code do bezpiecznego profilowania
Dział zatytułowany „Skonfiguruj Claude Code do bezpiecznego profilowania”Profilowanie wymaga dostępu do powłoki: skryptów benchmarku, node --cpu-prof, py-spy i psql. Zezwól z nazwy na polecenia benchmarku i profilerów, psql, pgrep i kill zatwierdzaj pojedynczo, a edycję katalogu z benchmarkiem i budżetem zablokuj. Agent, który może przepisać własny benchmark, zaliczy go, nie stając się szybszym.
{ "permissions": { "allow": [ "Bash(node perf/bench.mjs *)", "Bash(node perf/compare.mjs *)", "Bash(node --cpu-prof *)", "Bash(py-spy record *)", "Bash(npm test *)" ], "deny": [ "Edit(perf/**)" ] }}Edit(perf/**) blokuje tylko narzędzia plikowe. Polecenie powłoki nadal może zapisać coś w perf/, więc trwała ochrona siedzi w CI: bramka czyta budżet z gałęzi bazowej, a zmiany w perf/ wymagają akceptacji code ownera. Jak dopasowywane są reguły uprawnień, opisuje konfiguracja Claude Code.
Zacznij dochodzenie w trybie planowania (/plan albo Shift+Tab, aż tryb pokaże plan). Claude czyta wtedy kod i proponuje plan, zanim cokolwiek zmieni. Zmień tryb dopiero wtedy, gdy masz uszeregowaną listę hotspotów i się z nią zgadzasz.
Najpierw zapisz baseline i budżet
Dział zatytułowany „Najpierw zapisz baseline i budżet”-
Wybierz trzy najwolniejsze ścieżki widoczne dla użytkownika. Weź je z monitoringu, nie z intuicji. Jeśli nie masz jeszcze monitoringu, zacznij od endpointów, na które skarżą się użytkownicy, a potem skonfiguruj monitoring z Claude Code.
-
Poproś Claude Code o powtarzalny benchmark. Ma działać na lokalnej lub stagingowej kopii z danymi o kształcie produkcyjnym. Dziesięć wierszy w tabeli ukrywa każde zapytanie N+1.
-
Wpisz cel do
perf/budget.jsonsamodzielnie. Na przykład:/api/ordersp95 ≤ 300 ms. Cel ustalony, zanim zobaczysz poprawkę, nie dopasuje się po cichu do tego, co poprawka dała. -
Załaduj dane w skali produkcyjnej. Jeśli rozrzut z kroku 2 jest duży, najpierw napraw środowisko: zamknij inne obciążenia, przypnij wersję Node.js i używaj tego samego snapshotu danych w każdym przebiegu.
Profiluj hotspot zamiast zgadywać
Dział zatytułowany „Profiluj hotspot zamiast zgadywać”Daj Claude Code wynik profilera, a nie mgliste objawy. Poniższe polecenia tworzą pliki, które agent może przeczytać. Poproś go o podsumowanie ich skryptem, bo surowy profil CPU albo snapshot sterty jest dużo większy, niż chcesz mieć w oknie kontekstu.
// Czyste wyjście na SIGTERM, żeby Node.js zapisał profil CPU przed zatrzymaniemprocess.on('SIGTERM', () => process.exit(0));# Terminal: zapisz profil CPU V8, gdy benchmark obciąża serwernode --cpu-prof --cpu-prof-dir=perf/profiles dist/server.js &node perf/bench.mjs --endpoint /api/orders --runs 1kill %1 # SIGTERM: handler powyżej kończy proces czysto, więc powstaje plik .cpuprofile--cpu-prof zapisuje profil, gdy Node.js kończy działanie w normalny sposób. Bez handlera SIGTERM polecenie kill kończy proces bez zapisu i katalog perf/profiles/ zostaje pusty. Jeśli serwer ma już handler łagodnego zamykania, upewnij się, że kończy się wywołaniem process.exit(), zamiast dodawać drugi.
# Terminal: próbkuj działający proces przez py-spy (PyPI: py-spy)py-spy record --pid "$(pgrep -f 'uvicorn app.main')" --duration 30 \ --format speedscope --output perf/profiles/orders.json# Terminal: zapisz prawdziwy plan wolnego zapytania (rola tylko do odczytu, kopia stagingowa)psql "$STAGING_DATABASE_URL" -c \ "EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT ... FROM orders WHERE status = 'pending';" \ > perf/profiles/orders-plan.jsonJeśli trzy endpointy są wolne z niezależnych powodów, poproś Claude o równoległe dochodzenie z jednym subagentem na każdy. Każdy subagent zwraca tylko swoją uszeregowaną listę, więc główna rozmowa pozostaje mała. Wielokrotnego użytku perf-investigator z narzędziami tylko do odczytu zbudujesz według strony o własnych subagentach.
Naprawiaj wolne zapytania z planem przed i po
Dział zatytułowany „Naprawiaj wolne zapytania z planem przed i po”Dostęp do bazy to zwykle pierwszy podejrzany w serwisie typu CRUD, ale musi to potwierdzić profil. Gdy profil wskazuje czas zapytań, dwie klasyczne przyczyny to wzorzec N+1 i brakujący indeks. Obie wyraźnie widać w EXPLAIN ANALYZE: wiele małych, identycznych zapytań albo skan sekwencyjny dużej tabeli.
Przy powtarzalnej pracy z bazą możesz dać Claude Code bezpośredni dostęp tylko do odczytu przez Postgres MCP Pro (PyPI postgres-mcp, projekt społecznościowy Crystal DBA). Tryb restricted używa transakcji tylko do odczytu i limitów czasu wykonania, a serwer dodaje narzędzia do strojenia indeksów i planów. Ostatnie wydanie na PyPI to 0.3.0 z 2025-05-16 (stan na 2026-09-26), więc przypnij tę wersję i sprawdź aktywność repozytorium, zanim na nim oprzesz pracę. Łącz się też przez rolę bazodanową tylko do odczytu: tryb restricted to drugie zabezpieczenie, a nie jedyne.
# Terminal: dodaj serwer tylko dla tego projektu (zakres local), tylko do odczytuclaude mcp add postgres -e DATABASE_URI="$STAGING_DATABASE_URL" -- uvx postgres-mcp==0.3.0 --access-mode=restrictedPowłoka rozwija $STAGING_DATABASE_URL, zanim Claude Code je zobaczy, więc connection string trafia do twojej lokalnej konfiguracji MCP użytkownika, a nie do repozytorium. Nigdy nie dodawaj tego serwera z --scope project, bo ta opcja zapisuje .mcp.json, który trafi do commita.
Do jednorazowych planów równie dobrze działa psql przez narzędzie Bash. Inne serwery bazodanowe i ich przełączniki trybu tylko do odczytu znajdziesz na stronie o serwerach MCP dla baz danych.
O bezpieczeństwie decydują dwa szczegóły. EXPLAIN ANALYZE wykonuje instrukcję, więc UPDATE albo DELETE owiń w BEGIN; … ROLLBACK;. PostgreSQL odrzuca też CREATE INDEX CONCURRENTLY wewnątrz bloku transakcji, a tak właśnie większość narzędzi migracyjnych domyślnie uruchamia każdy plik. Przy zmianach schematu na dużą skalę przejdź do migracji bazy danych i kodu oraz pracy z bazą danych w Claude Code.
Znajdź wyciek pamięci ze snapshotów sterty
Dział zatytułowany „Znajdź wyciek pamięci ze snapshotów sterty”Wyciek wygląda jak pamięć, która rośnie przy identycznych żądaniach i nie wraca po odśmiecaniu. Czytanie kodu pod kątem wzorców wycieków daje listę podejrzanych, ale dwa snapshoty sterty zrobione w odstępie kilku minut pokazują, co faktycznie urosło.
# Terminal: rób snapshot sterty na żądanienode --heapsnapshot-signal=SIGUSR2 dist/server.js &kill -USR2 %1 # snapshot 1, po rozgrzaniunode perf/bench.mjs --endpoint /api/dashboard --runs 3kill -USR2 %1 # snapshot 2, po obciążeniuUtrzymaj rozmiar bundla frontendu w budżecie
Dział zatytułowany „Utrzymaj rozmiar bundla frontendu w budżecie”Ta sama idea budżetu działa w przeglądarce. Poproś Claude Code o dodanie rollup-plugin-visualizer (Vite i Rollup) dla mapy drzewa oraz size-limit dla kontroli, która zatrzymuje build, gdy chunk przekroczy limit. Najpierw ustaw limit na dzisiejszy rozmiar, a potem obniżaj go z każdą poprawką. Taka zapadka daje agentowi cel, a CI bramkę. Leniwe ładowanie każdej trasy potrafi stworzyć kaskadę małych żądań, więc powiedz agentowi, ile chunków może leżeć na ścieżce krytycznej.
Popraw jeden hotspot i udowodnij efekt
Dział zatytułowany „Popraw jeden hotspot i udowodnij efekt”Pracuj w osobnym worktree, żeby benchmark w głównym checkoucie pozostał czystą próbą kontrolną. claude -w perf-orders tworzy worktree gita i uruchamia w nim sesję.
Potem uniezależnij bramkę od agenta. perf/compare.mjs to deterministyczny skrypt, który CI może uruchamiać przy każdym merge’u do gałęzi głównej. Gdy zawiedzie, uruchomienie Claude Code w trybie nieinteraktywnym (claude -p) może wyjaśnić regresję bez prawa zapisu:
# CI albo nocne zadanie na zaufanej gałęzi głównej, po porażce compare.mjsclaude -p "perf/compare.mjs failed. Read perf/latest.json, perf/budget.json and git log -10. \Name the most likely commit and hotspot, citing the numbers. Do not change files." \ --permission-mode dontAsk \ --allowedTools "Read" "Grep" "Bash(git log *)" "Bash(node perf/compare.mjs *)" \ --max-budget-usd 2 --output-format json > perf/regression-report.jsonclaude -p startuje w trybie Manual (od v2.1.285 w trybie auto u zewnętrznych dostawców lub z wyłączoną telemetrią), więc --permission-mode dontAsk z dokładną listą --allowedTools ogranicza przebieg do tych narzędzi oraz odczytów w katalogach roboczych i poleceń tylko do odczytu. Uruchamiaj to wyłącznie na zaufanych refach, nigdy na kodzie z niezaufanego pull requesta, gdy w środowisku są sekrety. Plik workflow i jego uprawnienia opisuje strona o Claude Code w CI/CD.
Nie wkładaj benchmarku do hooka PostToolUse. Trwa minuty, odpala się po każdej edycji i uczy agenta stroić pod szum. Uruchom go raz, na końcu zadania, tam gdzie umieszcza go powyższy prompt.
Skąd wiesz, że poprawka wydajności jest prawdziwa?
Dział zatytułowany „Skąd wiesz, że poprawka wydajności jest prawdziwa?”Nie musisz czytać każdej linii diffa, żeby zaufać przyspieszeniu. Potrzebujesz pięciu dowodów dołączonych do pull requesta jako część jego pakietu dowodów:
| Teza | Dowód | Kto sprawdza |
|---|---|---|
| Jest szybciej | Wynik compare.mjs: baseline, nowe p95 i rozrzut, przy czym zysk przekracza dwukrotność rozrzutu | Bramka CI |
| Nadal działa poprawnie | Istniejący zestaw testów przechodzi, plus test dla każdego przepisanego zapytania | Bramka CI |
| Poprawiono właściwe miejsce | Profil lub plan sprzed poprawki wskazuje ten sam plik i funkcję, które zmienia diff | Recenzent, w dwie minuty |
| Nic innego nie zwolniło | Każdy endpoint z budget.json przechodzi, nie tylko docelowy | Bramka CI |
| Poprzeczka się nie przesunęła | perf/budget.json bez zmian albo zmieniony w osobnym commicie zaakceptowanym przez code ownera | Code owner |
Praca recenzenta sprowadza się do dwóch pytań: czy profil uzasadnia tę zmianę i czy budżet pozostał nietknięty? Autor podpisuje się pod dowodami, a code owner pod każdą zmianą budżetu. Jeśli potrzebujesz profilu obciążenia większego niż benchmark na jednej maszynie, przed wydaniem uruchom testy obciążeniowe i stresowe.
Co się psuje, gdy agent optymalizuje wydajność?
Dział zatytułowany „Co się psuje, gdy agent optymalizuje wydajność?”Agent zoptymalizował kod, który nigdy nie był gorący. Diff jest schludny, testy przechodzą, a p95 stoi w miejscu. Wyjście: cofnij zmianę i uruchom ponownie prompt z profilem. Przyjmuj tylko poprawki, których hotspot wskazuje profil, i odrzucaj wszystko oznaczone jako „unmeasured”.
Zysk jest mniejszy niż szum. Dwa przebiegi różnią się o 20% jeszcze przed zmianą, więc 10% „poprawy” nic nie znaczy. Wyjście: korzystaj z rozrzutu z baseline.json. Zwiększ liczbę iteracji, zatrzymaj obciążenia w tle i uruchamiaj bramkę na dedykowanym runnerze CI.
Benchmark został oszukany. Agent dodał cache pod dokładne żądanie z benchmarku, zmniejszył liczbę połączeń albo zmienił budżet. Wyjście: trzymaj Edit(perf/**) na liście deny, niech CI czyta budżet z gałęzi bazowej, a bench.mjs niech zmienia parametry żądań.
Poprawka N+1 dała gorsze zapytanie. Jeden JOIN przez pięć tabel może kosztować więcej niż pięć zaindeksowanych odczytów przy dużych zbiorach wyników. Wyjście: porównaj wyniki EXPLAIN (ANALYZE, BUFFERS) przed i po, a jako alternatywę poproś Claude o wersję z paczkowanym IN (...).
Migracja z indeksem padła albo zablokowała zapisy. CREATE INDEX CONCURRENTLY nie działa w bloku transakcji, a zwykłe CREATE INDEX blokuje zapisy do tabeli na czas budowy. Wyjście: przenieś indeks do osobnej, nietransakcyjnej migracji. Nieudana budowa współbieżna zostawia nieprawidłowy indeks: usuń go i uruchom migrację ponownie.
Sesja utonęła w profilu. Agent wczytał surowy .cpuprofile albo snapshot sterty, kontekst się zapełnił, a odpowiedzi stały się mgliste. Wyjście: /rewind do momentu przed odczytem i poproś o skrypt podsumowujący. Limity kontekstu szczegółowo opisuje optymalizacja wydajności Claude Code.
Instrumentacja sama stała się spowolnieniem. Wrappery mierzące czas na każdej funkcji mają własny narzut. Wyjście: doraźne pomiary czasu schowaj za flagą środowiskową i używaj profilerów próbkujących, takich jak --cpu-prof i py-spy, które nie zmieniają twojego kodu.