Wzorce refaktoryzacji
Otwierasz src/services/checkout.service.ts, aby dodać jedną regułę rabatu, i znajdujesz pojedynczą funkcję processOrder długą na 340 linii: inline’owa walidacja, trzy zagnieżdżone gałęzie if dla typu płatności, obliczanie cen, zapis do magazynu i wysyłka e-maila — wszystko dzielące lokalne zmienne. Istniejący zestaw testów Vitest jest zielony, ale nikt nie chce tego ruszać, bo każda zmiana może po cichu zepsuć ścieżkę, która odpala się tylko na produkcji. To dokładnie ta sytuacja, w której Claude Code zarabia na siebie: potrafi najpierw scharakteryzować bieżące zachowanie w testach, a potem przenosić kod w małych, odwracalnych krokach, które możesz zweryfikować po każdym z nich.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- Pętlę refaktoryzacji test-first, która utrzymuje zielony zestaw testów na każdym kroku
- Gotowy do skopiowania prompt Extract Method, który nazywa prawdziwe pliki i uruchamia twój zestaw testów, aby udowodnić, że zachowanie się nie zmieniło
- Prompt, który zastępuje
switchpo typie płatności wzorcem Strategy i fabryką - Prompt do modernizacji starszego kodu (callbacki na
async/await), który aktualizuje miejsca wywołań i testy razem - Tryby awarii, które naprawdę kąsają podczas refaktoryzacji wspomaganej przez AI, i jak się z nich pozbierać
Pętla refaktoryzacji
Dział zatytułowany „Pętla refaktoryzacji”Refaktoryzacja z Claude Code to nie „poproś go, żeby to posprzątał”. To zdyscyplinowana pętla, w której testy są kontraktem, a każdy krok jest na tyle mały, że da się go przejrzeć:
-
Przypnij zachowanie. Wygeneruj testy charakteryzacyjne dla obecnego kodu, aby każda zmiana zachowania ujawniła się jako czerwony test. Jeśli ten obszar ma już dobre pokrycie, przejdź do kroku 2.
-
Wykonaj jedną nazwaną transformację. Wyciągnij metodę, wprowadź obiekt parametrów, zastąp warunek — jeden ruch na turę, nie przepisywanie od zera.
-
Zweryfikuj. Uruchom zestaw testów, kontroler typów i linter. Zielony oznacza, że ruch zachował zachowanie.
-
Zacommituj, potem powtórz. Jedna refaktoryzacja na commit, aby zły krok był jednoliniowym
git revert, a nie projektem archeologicznym.
Nieoczywista część to krok 1. Poproś Claude, aby przeczytał prawdziwy plik, zanim cokolwiek napisze, tak by rozumował o kodzie, który istnieje, a nie o kodzie, który zakłada, że istnieje.
Extract Method: koń pociągowy
Dział zatytułowany „Extract Method: koń pociągowy”Większość problemów typu „ta funkcja jest za długa” rozwiązuje się przez wyciągnięcie spójnych bloków do nazwanych funkcji. Wygrana z Claude Code polega na tym, że poprawnie przeplata współdzielone zmienne lokalne i aktualizuje oryginał, aby wywoływał nowe funkcje — żmudna, podatna na błędy część.
Pokaż mu ten sam koncept w swoim języku. Interakcja jest identyczna; różnią się tylko idiomy:
In src/services/checkout.service.ts, processOrder does five jobs. Extract threepure functions in the same file: validateOrder(input), calculatePricing(cart),and reserveInventory(items). Leave the email send and the DB write inprocessOrder as the orchestrator. Keep every existing type; do not widen anytype to `any`. Then run `npx vitest run checkout` and `npm run type-check`and report both results.In app/services/checkout.py, refactor process_order by extractingvalidate_order(payload), calculate_pricing(cart), and reserve_inventory(items)as module-level functions with type hints. Keep process_order as theorchestrator. Run `pytest tests/test_checkout.py -q` and `mypy app/services`and show me the output before and after.In lib/shop/checkout.ex, process_order/1 is doing too much. Extractvalidate_order/1, calculate_pricing/1, and reserve_inventory/1 as privatefunctions, and pipe them together in process_order/1. Keep the existingtypespecs. Run `mix test test/shop/checkout_test.exs` and report the result.Ta ostatnia linijka ma znaczenie. Najczęstszym trybem awarii w refaktoryzacji AI jest edytowanie testu przez model, aby przeszedł. Polecenie, by zamiast tego wycofał zmianę i zdał raport, utrzymuje test jako źródło prawdy.
Zastąp warunek polimorfizmem
Dział zatytułowany „Zastąp warunek polimorfizmem”switch po typie płatności, który co kwartał zyskuje nową gałąź, to podatek za utrzymanie. Wzorzec Strategy przenosi każdy przypadek za wspólny interfejs. To zmiana w wielu plikach, gdzie kolejność ma znaczenie, więc uruchom ją najpierw w trybie planowania (Shift+Tab, aby przejść do trybu planowania) i zatwierdź plan przed jakimikolwiek edycjami.
Behawioralny test dobrej refaktoryzacji Strategy jest prosty: istniejący zestaw testów payments powinien przejść bez żadnych edycji, ponieważ tylko przeniosłeś logikę, a nie ją zmieniłeś. Jeśli test wymaga edycji, refaktoryzacja zmieniła zachowanie i powinieneś się zatrzymać.
Modernizuj starszy kod
Dział zatytułowany „Modernizuj starszy kod”API oparte na callbackach, łańcuchy .then() i moduły CommonJS to typowe cele modernizacji. Pułapka polega na zaktualizowaniu implementacji przy pozostawieniu miejsc wywołań lub testów w starej formie. Spraw, by Claude zaktualizował je razem.
Aktualnym, realistycznym celem na poziomie frameworka jest migracja React 18 na 19. React 19 to obecna stabilna wersja główna, więc opieraj się na niej, a nie na starszym skoku:
Create a migration plan to move this app from React 18 to React 19:- Identify uses of the removed legacy APIs (ReactDOM.render, findDOMNode, legacy Context, string refs) with Grep and list each occurrence.- Map any forwardRef components that can drop the wrapper now that ref is a prop.- Note where the `use()` hook or Actions would simplify existing data-fetching.Do not edit anything yet. Output the plan as a checklist I approve before youstart, smallest-risk items first.Dzielenie rozdętego serwisu
Dział zatytułowany „Dzielenie rozdętego serwisu”Gdy klasa wchłonęła niepowiązane odpowiedzialności, podziel ją — ale weryfikuj szwy kontrolerem typów, a nie na oko.
src/services/user.service.ts has grown to ~900 lines and mixes authentication,profile updates, and notification sending. Plan a split into AuthService,ProfileService, and NotificationService. For each: list which existing methodsmove, which private helpers they need, and which call sites must be updated.Keep one thin UserService facade that delegates, so external imports do notbreak in this PR. Implement only after I approve the plan, one service percommit. Run `npm run type-check` after each service to catch a missed reference.Gdy to nie działa
Dział zatytułowany „Gdy to nie działa”Claude edytuje test, aby przeszedł. To kardynalna awaria. Zawsze dołączaj do promptu „if a test fails, revert your change and report which assertion broke” i przeglądaj diff pod kątem edycji plików testowych, o które nie prosiłeś. Test jest kontraktem; implementacja porusza się wokół niego.
Refaktoryzacja „działa”, ale zepsuła się ścieżka istniejąca tylko na produkcji. Twoje testy charakteryzacyjne nie pokryły tej ścieżki. Przed refaktoryzacją kodu o wysokiej stawce poproś Claude, aby najpierw wyliczył gałęzie („List every distinct code path through processOrder and the input that triggers it”) i napisał test dla każdej z nich, zanim cokolwiek ruszysz.
Zmiana big-bang ląduje jako jeden gigantyczny diff. Zatrzymaj się i zresetuj: git reset --hard <last-known-good-commit>. Potem ponów pracę z jawną instrukcją „one refactoring per commit, run the suite between each”. Małe kroki sprawiają, że refaktoryzacja AI jest bezpieczna do przeglądu.
Model gubi konwencje bazy kodu w trakcie refaktoryzacji. Zaczął wymyślać nowy format błędów lub inny układ folderów. Skieruj go z powrotem na konkretną kotwicę: „Match the error-handling pattern in src/api/routes/orders.ts exactly — same error class, same response shape.” Zakodowanie tych konwencji w pliku CLAUDE.md utrzymuje spójność każdej sesji.
„Optymalizacja” wydajności zmienia wyniki. Jeśli poprosiłeś o zastąpienie pętli O(n^2), potraktuj to jako refaktoryzację pod testem: najpierw przypnij wynik testem, podmień algorytm, potem potwierdź, że test nadal przechodzi, i zmierz wydajność na realnych liczbach (console.time lub twój harness do benchmarków), zamiast ufać asymptotycznej deklaracji.