Refaktoryzacja kodu legacy z Cursor
Odziedziczyłeś 5-letnie API Express. Piętnaście tysięcy linii JavaScript w 50 plikach. Callbacki zagnieżdżone cztery poziomy w głąb. Logika biznesowa splątana w handlerach route. Zero testów. I 10 000 codziennych użytkowników zależnych od każdego dziwnego zachowania, włącznie z tymi, których nikt nie udokumentował. Twoje zadanie: zmodernizować to bez jednej minuty przestoju.
To jest scenariusz, w którym refaktoryzacja wspierana przez AI naprawdę błyszczy — nie dlatego, że AI jest mądrzejsze od ciebie, ale dlatego, że może przeczytać tysiące linii nieznanego kodu w sekundy i wydobyć wzorce, zależności i ukryte sprzężenia, których zmapowanie zajęłoby człowiekowi dni. Sztuczka polega na tym, aby wiedzieć, jak sekwencjonować pracę, żeby nigdy nie zepsuć tego, co już działa.
Co wyniesiemy
Dział zatytułowany „Co wyniesiemy”- Systematyczny przepływ pracy do zrozumienia bazy kodu legacy używając trybu Ask przed dotknięciem jakiegokolwiek kodu
- Prompt “testu charakteryzacyjnego”, który generuje testy przechwytujące istniejące zachowanie, włącznie z bugami
- Technikę edycji inline do konwersji callbacków na async/await po jednej funkcji na raz bez zmiany zachowania
- Prompt ekstrakcji serwisu, który oddziela logikę biznesową od handlerów HTTP z mechaniczną precyzją
- Strategię checkpointów, która daje bezpieczny punkt rollbacku po każdym udanym kroku refaktoryzacji
Przepływ pracy
Dział zatytułowany „Przepływ pracy”Krok 1: Zmapuj bazę kodu z trybem Ask
Dział zatytułowany „Krok 1: Zmapuj bazę kodu z trybem Ask”Opieraj się pokusie rozpoczęcia zmieniania kodu. Użyj najpierw trybu Ask, aby zbudować model mentalny tego, z czym pracujesz. Tryb Ask jest tylko do odczytu — przeszukuje twoją bazę kodu i odpowiada na pytania bez modyfikowania czegokolwiek.
To daje ci mapę, zanim zaczniesz nawigować. Zapisz wynik — będziesz się do niego wielokrotnie odwoływać.
Krok 2: Prześledzenie krytycznych ścieżek przed dotknięciem czegokolwiek
Dział zatytułowany „Krok 2: Prześledzenie krytycznych ścieżek przed dotknięciem czegokolwiek”Najbardziej niebezpieczne błędy refaktoryzacji zdarzają się, gdy zmieniasz kod na ścieżce, której nie rozumiesz w pełni. Wybierz trzy najważniejsze przepływy użytkownika (np. logowanie, checkout, płatność) i prześledzenie je od końca do końca.
@src/routes/checkout.js @src/controllers/orders.js @src/models/order.js @src/services/payment.js
Prześledzenie kompletnego przepływu checkout od żądania HTTP do zapisu w bazie danych:1. Jaka jest dokładna sekwencja wywołań funkcji?2. Gdzie dane żądania są walidowane (jeśli w ogóle)?3. Jakie transakcje bazodanowe są używane i gdzie mogą zostawić częściowy stan?4. Co się dzieje, jeśli wywołanie API płatności zawiedzie w połowie?5. Czy są jakieś race conditions przy współbieżnych checkoutach?
Narysuj diagram sekwencji w składni Mermaid.Krok 3: Generowanie testów charakteryzacyjnych
Dział zatytułowany „Krok 3: Generowanie testów charakteryzacyjnych”Przed refaktoryzacją pojedynczej linii, przechwyć istniejące zachowanie w testach. To nie są testy jednostkowe dla “poprawnego” zachowania — to są testy charakteryzacyjne, które dokumentują, co kod faktycznie robi, bugi i wszystko. Jeśli twoja refaktoryzacja zmieni wynik testu, wiesz, że zmieniłeś zachowanie.
Ten prompt wyraźnie mówi AI, aby nie “ulepszało” zachowania podczas tworzenia testów, co jest częstym trybem awarii. Chcesz testów, które przechodzą na istniejącym kodzie, kropka. Naprawianie bugów przychodzi później, po refaktoryzacji, kiedy możesz świadomie zmienić oczekiwania testów.
Krok 4: Refaktoryzacja po jednej funkcji na raz z edycjami inline
Dział zatytułowany „Krok 4: Refaktoryzacja po jednej funkcji na raz z edycjami inline”Teraz zaczyna się faktyczna refaktoryzacja. Użyj Cmd+K (edycja inline) do chirurgicznych zmian pojedynczych funkcji. To jest szybsze i bardziej kontrolowane niż tryb Agent dla refaktoryzacji pojedynczej funkcji, bo utrzymuje zmiany w zakresie dokładnie tego kodu, który zaznaczyłeś.
Zaznacz pojedynczą funkcję opartą na callbackach w edytorze, naciśnij Cmd+K i opisz transformację:
Przekonwertuj tę funkcję opartą na callbackach na async/await.Zachowaj dokładnie to samo zachowanie -- te same wartości zwracane, te same odpowiedzi błędów,te same efekty uboczne. Nie zmieniaj sygnatury funkcji ani nie dodawaj nowych parametrów.Nie dodawaj logowania ani nie ulepszaj komunikatów błędów. Tylko czysta mechaniczna konwersja.Po każdej konwersji uruchom swoje testy charakteryzacyjne. Jeśli przechodzą, commituj. Jeśli nie przechodzą, cofnij za pomocą Cmd+Z i zbadaj dlaczego.
Krok 5: Ekstrakcja warstwy serwisu z trybem Agent
Dział zatytułowany „Krok 5: Ekstrakcja warstwy serwisu z trybem Agent”Gdy kod jest zmodernizowany do async/await, następną dużą wygraną jest oddzielenie logiki biznesowej od handlerów route. To tutaj tryb Agent błyszczy — może tworzyć nowe pliki, przenosić kod, aktualizować importy i weryfikować wynik za jednym razem.
To oddzielenie czyni kod testowalnym w izolacji — możesz testować serwis bez uruchamiania serwera HTTP. Czyni również przyszłe zmiany bezpieczniejszymi, bo reguły biznesowe są w jednym miejscu zamiast rozrzucone po handlerach route.
Krok 6: Dodawanie TypeScript przyrostowo
Dział zatytułowany „Krok 6: Dodawanie TypeScript przyrostowo”Nie musisz konwertować całej bazy kodu na TypeScript naraz. Cursor sprawia, że przyrostowa adopcja jest prosta:
@src/services/order-service.ts @src/models/order.js
Przekonwertuj src/models/order.js na TypeScript:1. Zmień nazwę na order.ts2. Dodaj interfejsy dla wszystkich kształtów danych (Order, OrderItem, OrderStatus)3. Dodaj właściwe typy do wszystkich parametrów funkcji i wartości zwracanych4. Użyj ścisłej konfiguracji TypeScript -- żadnych typów `any`, włącz strictNullChecks5. Eksportuj interfejsy, aby warstwa serwisu mogła ich użyć6. Zaktualizuj importy we wszystkich plikach, które odwołują się do tego modułu
Upewnij się, że tsconfig.json pozwala na współistnienie plików .js i .ts (allowJs: true).Konwertuj pliki bottom-up: najpierw modele, potem serwisy, potem controllery, potem routes. Każda warstwa zależy od tej poniżej, więc typowanie fundamentów najpierw daje największą wartość.
Krok 7: Optymalizacja niebezpiecznych zapytań
Dział zatytułowany „Krok 7: Optymalizacja niebezpiecznych zapytań”Z testami na miejscu i czystą architekturą, możesz teraz bezpiecznie zająć się problemami wydajności, które każda baza kodu legacy gromadzi.
@src/services @src/models
Znajdź i napraw trzy najgorsze wzorce zapytań do bazy danych w tej bazie kodu:1. Zapytania N+1 (ładowanie powiązanych danych w pętli zamiast join)2. Brakujące klauzule WHERE w instrukcjach UPDATE/DELETE3. Zapytania, które ładują całe tabele, gdy potrzebują tylko konkretnych kolumn
Dla każdej naprawy:- Pokaż SQL przed/po- Wyjaśnij wpływ na wydajność- Dodaj indeks, jeśli plan zapytania na tym skorzysta- Utwórz plik migracji dla wszelkich zmian schematu
Nie zmieniaj danych zwracanych przez żadną funkcję publiczną -- tylko sposób, w jaki są pobierane.Kiedy to się zepsuje
Dział zatytułowany „Kiedy to się zepsuje”AI “ulepsza” zachowanie podczas mechanicznej refaktoryzacji. To jest najczęstsza awaria. Prosisz o konwersję callback-na-async, a AI również dodaje walidację wejścia, zmienia komunikaty błędów lub “naprawia” przypadek brzegowy. Zawsze starannie sprawdzaj różnice. Jeśli testy charakteryzacyjne nie przechodzą, odrzuć zmianę — AI zmieniło zachowanie, nie tylko strukturę.
Testy przechodzą, ale produkcja się psuje. Testy charakteryzacyjne pokrywają tylko ścieżki, które przetestowałeś. Kod legacy często ma nieudokumentowane zachowanie wyzwalane przez konkretne wzorce danych. Wdrażaj za flagą funkcji i porównuj stare i nowe ścieżki kodu w produkcji z prawdziwym ruchem przed przełączeniem.
Tryb Agent przepisuje za dużo. Gdy odwołujesz się do całego folderu @src, Agent czasami decyduje się refaktoryzować wszystko naraz. Bądź konkretny: odwołuj się do pojedynczych plików, daj wyraźne granice zakresu i dodaj “Nie modyfikuj żadnego pliku poza X” do swojego promptu.
Konwersja TypeScript wydobywa ukryte błędy typów. To jest faktycznie funkcją, nie bugiem. Gdy TypeScript złapie niemożliwy typ, to często prawdziwy bug. Dodaj to do swojego backlogu, ale nie naprawiaj podczas fazy refaktoryzacji — napraw oddzielnie z własnym testem.
Cykliczne zależności po ekstrakcji serwisu. Jeśli AI tworzy serwisy, które importują się nawzajem, masz problem projektowy. Użyj trybu Ask do analizy grafu zależności i przerwania cyklu przez ekstrakcję współdzielonych typów do osobnego modułu.