Strategie refaktoryzacji w Cursorze
ENBezpieczna strategia refaktoryzacji w Cursorze zamienia jedną dużą przebudowę w serię małych ruchów zachowujących działanie kodu. Każdy trafia do commita dopiero wtedy, gdy przechodzą niezmienione testy, sprawdzanie typów i kontrola publicznego API. Edycje robi agent, a zamrożone testy, raport API i testy mutacyjne dowodzą, że nic się nie zmieniło, więc recenzent sprawdza dowody zamiast czytać każdą linię.
Ta strona jest dla programistów i tech leadów, którzy utrzymują kod mający już testy. Twój user-service.ts urósł do 800 linii i obsługuje uwierzytelnianie, profile, preferencje powiadomień oraz usuwanie kont. Każda zmiana wchodzi komuś w drogę, a po ostatnim dużym podziale padły trzy zależne serwisy i dojście do stabilności zajęło tydzień. Chcesz, żeby agent wykonał mechaniczną pracę bez powtórki tamtego tygodnia.
Jeśli kod nie ma testów, zacznij od refaktoryzacji kodu legacy w Cursorze, która najpierw buduje siatkę bezpieczeństwa. Przy wymianie frameworka lub biblioteki zajrzyj do migracji frameworka.
Co daje ci refaktoryzacja prowadzona przez agenta
Dział zatytułowany „Co daje ci refaktoryzacja prowadzona przez agenta”- Kontrakt refaktoryzacji: pięć bramek z dokładnymi komendami, które definiują „zachowanie się nie zmieniło”
- Pętlę „jeden ruch na commit”, którą agent wykonuje, z promptami do wklejenia bez zmian
- Tabelę decyzyjną dla typowych wzorców refaktoryzacji: co oddać agentowi, co codemodowi i jakiej dodatkowej bramki wymaga każdy z nich
- Podsumowanie dowodów, które recenzent zatwierdzi bez czytania całego diffu
- Sposoby wychodzenia z typowych wpadek agenta, zaczynając od edytowania testów, żeby było zielono
Kiedy refaktoryzacja agentem jest bezpieczna?
Dział zatytułowany „Kiedy refaktoryzacja agentem jest bezpieczna?”Refaktoryzacja zmienia strukturę i zachowuje działanie. Z tej definicji wynika test: jeśli te same testy przechodzą na nowej strukturze, a publiczny interfejs jest identyczny, to zachowanie się nie zmieniło. Ryzyko z agentem polega na tym, że może on ruszyć obie strony tego równania. Potrafi poprawiać test, aż przejdzie, albo „uporządkować” ścieżkę błędu przy przenoszeniu kodu. Dlatego kontrakt ustala przed pierwszą edycją, czego agentowi nie wolno dotykać.
| Bramka | Czego dowodzi | Komenda (przykład w TypeScripcie) |
|---|---|---|
| Zamrożone testy | Wyrocznia się nie przesunęła | git diff --name-only refactor-base -- '*.test.ts' '*.spec.ts' nic nie wypisuje |
| Zielony zestaw testów | Zachowanie przypięte testami jest nienaruszone | npm test |
| Typy i lint | Nie ma zepsutych wywołań ani martwych importów | npx tsc --noEmit i npm run lint |
| Niezmienione publiczne API | Konsumenci widzą te same eksporty i sygnatury | npx api-extractor run --local, potem git diff na raporcie w etc/ |
| Wystarczająco mocna wyrocznia | Testy zauważyłyby zmianę zachowania | npx stryker run --mutate "src/services/user-service.ts,src/services/user/**/*.ts", wynik nie niższy niż bazowy |
Najważniejsza jest pierwsza bramka. Refaktoryzacja, która przy okazji edytuje testy, nie ma już niezależnej kontroli — to argument stojący za ochroną wyroczni. Jeśli test naprawdę musi się zmienić, na przykład dlatego, że importował prywatny helper po ścieżce, zrób tę zmianę w osobnym commicie przed refaktoryzacją i przejrzyj ją sam. Punkt odniesienia oznacz tagiem dopiero po tym commicie (krok 1 poniżej), żeby bramka zamrożonych testów porównywała z zatwierdzonymi testami i mogła przejść.
Ustal kontrakt refaktoryzacji przed pierwszą edycją
Dział zatytułowany „Ustal kontrakt refaktoryzacji przed pierwszą edycją”-
Zacznij od zielonego punktu wyjścia. Uruchom cały zestaw testów na czystej gałęzi i zrób commit. Refaktoryzacja, która startuje na czerwono, niczego nie udowodni. Następnie zacommituj ewentualną zatwierdzoną zmianę samych testów i oznacz punkt, z którym porównują się wszystkie bramki:
git tag refactor-base. -
Sprawdź, czy testy wychwyciłyby zmianę. Pokrycie linii mówi, że linia się wykonała, a nie że test by padł, gdyby się zmieniła. Najpierw zainstaluj właściwy pakiet, bo samo
npx strykerpobiera przestarzały pakietstrykerw wersji 0.x:npm i -D @stryker-mutator/core, a potemnpx stryker init, które zapisze konfigurację i wybierze runner testów (sprawdzone na wersji 10.0.0, 2026-09-30). Uruchom go na kodzie, który zamierzasz przenosić, z tym samym globem co bramka końcowa, żeby oba wyniki obejmowały ten sam kod:npx stryker run --mutate "src/services/user-service.ts,src/services/user/**/*.ts". Zapisz wynik mutacyjny oraz liczbę zabitych i ocalałych mutantów. Mutanty, które przeżyły w przenoszonym kodzie, to luki; zamknij je testami charakteryzującymi przed refaktoryzacją. Jak czytać raport, wyjaśnia strona o sile wyroczni. -
Zrób migawkę publicznego interfejsu. Dla pakietu z jednym punktem wejścia zainstaluj
npm i -D @microsoft/api-extractor(7.59.3 na 2026-09-30; nazwaapi-extractorbez zakresu to w npm pusty placeholder, a nie to narzędzie). Narzędzie czyta zbudowane deklaracje, więc najpierw wygeneruj je buildem albo przeznpx tsc --declaration --emitDeclarationOnly, potem uruchomnpx api-extractor initi ustaw wapi-extractor.jsonpolemainEntryPointFilePathna plik.d.tspunktu wejścia.npx api-extractor run --localzapisuje raport.api.mdze wszystkimi eksportami i sygnaturami, domyślnie w kataloguetc/. Zacommituj go. W aplikacji odpowiednikiem są testy kontraktowe lub testy tras. -
Napisz regułę refaktoryzacji. Dodaj regułę projektu na czas refaktoryzacji, żeby każda sesja agenta dziedziczyła te same ograniczenia. Utwórz ją jako regułę projektu (gdzie Cursor ją przechowuje, opisuje podlinkowana strona) i usuń po scaleniu refaktoryzacji:
During the user-service refactor:- Never edit files matching *.test.ts or *.spec.ts. If a test fails, stop and report it.- One structural move per step. No renames, formatting or "cleanups" in the same step.- Keep src/services/user-service.ts exporting everything it exports today (re-export).- After each step run: npx tsc --noEmit && npm test. Report the result before continuing. -
Poproś o listę ruchów, nie o kod. Użyj pierwszego promptu poniżej w Plan Mode, który tworzy plan przed napisaniem jakiegokolwiek kodu. Ty przeglądasz plan, a plan staje się specyfikacją dla reszty sesji.
Wykonuj refaktoryzację po jednym ruchu na commit
Dział zatytułowany „Wykonuj refaktoryzację po jednym ruchu na commit”Po zatwierdzeniu planu przełącz się na tryb Agent i realizuj plan krok po kroku. Pętla jest celowo nudna:
- Agent wykonuje jeden ruch z planu i aktualizuje importy, których ten ruch dotyczy.
- Agent uruchamia
npx tsc --noEmit && npm testi raportuje wynik. - Rzucasz okiem na
git diff --stat. Lista plików powinna zgadzać się z listą z planu dla tego kroku. Więcej plików oznacza, że krok był za duży. - Robisz commit z numerem kroku w komunikacie. Nieudany krok 7 z 10 kosztuje wtedy jeden
git revert, a nie całą refaktoryzację.
Checkpointy Cursora to lokalne migawki zmian agenta; commity to zapis, który przetrwa sesję i który widzi recenzent. Jak obie warstwy współpracują, opisuje strona o checkpointach i gałęziach.
Który wzorzec refaktoryzacji powinien wykonać agent?
Dział zatytułowany „Który wzorzec refaktoryzacji powinien wykonać agent?”Nie każda refaktoryzacja to praca dla agenta. Czysto mechaniczną zmianę w 200 plikach lepiej zrobi narzędzie, które nie potrafi być kreatywne. Agent ma przewagę tam, gdzie zmiana wymaga oceny: grupowania funkcji, przepisywania przepływu sterowania, aktualizacji wywołań, które nie wyglądają tak samo.
| Wzorzec | Kto robi edycję | Główne ryzyko dla zachowania | Dodatkowa bramka ponad kontrakt |
|---|---|---|---|
| Zmiana nazwy symbolu | Rename Symbol w edytorze albo codemod; agent tylko do odwołań w stringach (trasy, dokumentacja, klucze logów) | Dynamiczne odwołania po nazwie | git grep -n oldName zwraca tylko zamierzone trafienia |
| Wydzielenie modułu | Agent, jedna grupa funkcji na krok | Współdzielony stan modułu, efekty uboczne kolejności importów | npx knip --exports --cycles nie pokazuje nowego cyklu ani osieroconego eksportu |
| Zmiana sygnatury (argumenty pozycyjne na obiekt opcji) | Agent dla miejsc wywołań albo ts-morph przy bardzo dużej ich liczbie | Zamienione argumenty, które nadal przechodzą sprawdzanie typów | Sprawdzanie typów plus jeden test na gałąź każdego zmienionego wywołania |
Callbacki na async/await | Agent, jeden moduł na krok | Połknięte lub dwukrotnie rzucone błędy, zmieniona kolejność | Wynik mutacyjny modułu nie spada |
| Przeniesienie kodu między warstwami | Agent po zatwierdzeniu planu | Po cichu złamane reguły warstw | Reguła dependency-cruisera, zob. funkcje dopasowania |
Zmiana sygnatury to miejsce, w którym kod ze sprawdzanymi typami potrafi oszukać: createUser(email, password, firstName, lastName, role, teamId) z zamienionymi firstName i lastName w jednym wywołaniu nadal się kompiluje. Obiekt opcji usuwa tę klasę błędów na przyszłość, ale tylko wtedy, gdy teraz każde wywołanie zostanie przeniesione poprawnie.
Zamień wartość bazową 81% na wynik zapisany na etapie kontraktu.
Porównaj dwa projekty refaktoryzacji w równoległych worktree
Dział zatytułowany „Porównaj dwa projekty refaktoryzacji w równoległych worktree”Gdy są dwa sensowne kształty, na przykład podział według funkcji albo według warstw, nie rozstrzygaj tego w teorii. Worktrees w Cursorze pozwalają agentowi pracować w izolowanej kopii repozytorium Git, więc dwa agenty mogą zbudować po jednym wariancie z tego samego punktu wyjścia, nie wchodząc sobie w pliki. Oceń oba tymi samymi bramkami, a potem tym, co diff robi z grafem zależności: zwykle wygrywa wersja z mniejszą liczbą importów między modułami i bez nowych cykli. Zostaw jedną, a drugi worktree usuń razem z gałęzią.
Jak udowodnić, że zachowanie się nie zmieniło, bez czytania każdej linii
Dział zatytułowany „Jak udowodnić, że zachowanie się nie zmieniło, bez czytania każdej linii”Na końcu recenzent powinien móc zatwierdzić zmianę na podstawie dowodów i krótkiej wyrywkowej kontroli. Poproś agenta, żeby je zebrał — i żeby uruchomił bramki, zamiast je opisywać.
Recenzent sprawdza wtedy ręcznie trzy rzeczy: listę zmian „niebędących czystym przeniesieniem”, pierwszy commit (czy nowa struktura zgadza się z planem?) i jeden losowo wybrany krok. Resztę pokrywają bramki, które zaświeciłyby się na czerwono. Jak takie podsumowanie wpisuje się w zespołową politykę przeglądów, opisuje pakiet dowodów. Jeśli twój zespół używa Bugbota, dokłada on automatyczny przegląd PR; uzupełnia bramki, ale ich nie zastępuje. Szersze warstwy przeglądu opisuje strona o code review w Cursorze.
Wstaw te bramki również do CI. Zielony wynik lokalnie dowodzi stanu maszyny agenta; te same komendy na pull requeście dowodzą stanu gałęzi.
Co się psuje przy refaktoryzacji agentem i jak z tego wyjść
Dział zatytułowany „Co się psuje przy refaktoryzacji agentem i jak z tego wyjść”Agent edytuje test, żeby przeszedł. Bramka zamrożonych testów wypisuje nazwę pliku. Przywróć go przez git checkout refactor-base -- path/to/file.test.ts, a potem zapytaj agenta, dlaczego test padł. Niezmieniony test, który nie przechodzi, oznacza, że ruch zmienił zachowanie — to jest wynik, nie przeszkoda.
Jeden krok dotyka znacznie więcej plików, niż mówił plan. Wychwyci to git diff --stat. Najpierw sprawdź git status, bo następna komenda odrzuca wszystkie niezacommitowane zmiany. Potem uruchom git restore . && git clean -fd -- src/ (albo git reset --hard <commit-poprzedniego-kroku> && git clean -fd): samo git restore zostawia nowe, nieśledzone pliki, takie jak authentication.ts, a przestarzały moduł może nadal przechodzić tsc i testy. Następnie podziel krok: „przenieś trzy funkcje” zamiast „przenieś całą grupę uwierzytelniania”.
Zachowanie zmienia się w ramach „sprzątania”. Agenty lubią porządkować przy okazji przenoszenia: catch, który teraz loguje i idzie dalej, wartość domyślna dodana „na wszelki wypadek”. Zauważa to bramka testów mutacyjnych, bo osłabiona ścieżka błędu zostawia mutanty, które przeżyły. Cofnij krok i powtórz go z „move verbatim” w prompcie.
Po wydzieleniu modułu pojawiają się cykliczne importy. Typowa przyczyna to plik barrel (index.ts), z którego importują nowe moduły, podczas gdy on sam je reeksportuje. npx knip --cycles wskazuje pętlę. Wewnątrz pakietu importuj z konkretnego modułu, a barrel zostaw tylko dla zewnętrznych konsumentów.
Stary kod zostaje obok nowej kopii. Agent czasem kopiuje zamiast przenosić, a obie wersje przechodzą testy. npx knip --exports wskazuje osierocony eksport. W skali repozytorium warto to obserwować: raport GitClear z czerwca 2026 r. „The Maintainability Gap”, znany tu z fragmentu w wynikach wyszukiwania (źródło wtórne, zależność korelacyjna), pokazał spadek udziału przenoszonego kodu z 13% do 3,8% zmienionych linii przy wzroście kopiowania o 41%. Zob. jak utrzymać w zdrowiu kod pisany przez agentów.
Długa sesja odpływa od planu. Po wielu krokach agent zaczyna łączyć ruchy. Otwórz nowy czat, wklej plan i wskaż następny krok; commity powiedzą mu, co jest już zrobione.
Gałęzie kolegów kolidują z refaktoryzacją. Zapowiedz refaktoryzację, trzymaj każdy pull request na kilku krokach i szybko je scalaj. Reeksporty ze starego pliku pozwalają gałęziom w toku kompilować się podczas rebase’u.
Dokąd dalej z refaktoryzacją w Cursorze
Dział zatytułowany „Dokąd dalej z refaktoryzacją w Cursorze”- Refaktoryzacja kodu legacy w Cursorze — gdy kod nie ma jeszcze testów do zamrożenia
- Wzorce testowania w Cursorze — zestaw testów, na którym opiera się ten workflow
- Strategie dla dużych baz kodu — zawężanie promptów i kontekstu, gdy refaktoryzacja obejmuje duże repozytorium
- Refaktoryzacja z Claude Code i refaktoryzacja z Codeksem — ten sam kontrakt w pozostałych narzędziach