Modernizacja legacy na dużą skalę z Codexem
Codex modernizuje legacy falami, a nie jednym przepisaniem. Ocena w trybie tylko do odczytu szereguje moduły według ryzyka, testy charakteryzacyjne utrwalają obecne zachowanie, warstwy niskiego ryzyka migrują w równoległych worktree, a najtrudniejsze moduły trafiają do zadań Codex cloud z dwiema–czterema próbami. Każda fala trafia na produkcję z tymi samymi dowodami: przechodzące, niezmienione testy, bramki typów i zapadka liczby plików.
Twój zespół utrzymuje 80 000 linii JavaScriptu z 2018 roku: callbacki zamiast async/await, komponenty klasowe Reacta i testy pokrywające ułamek kodu. Zarząd chce TypeScriptu „bez regresji na produkcji”, a ostatnie przepisanie big-bang trwało pół roku i nic nie dostarczyło. Ta strona jest dla developera, który prowadzi taką migrację w Codexie, i dla tech leada, który musi udowodnić bezpieczeństwo każdej fali bez czytania każdego zmigrowanego pliku.
Sama metoda (cięcie na kawałki, fale, dowody przed przełączeniem) nie zależy od narzędzia i opisuje ją strona modernizacja legacy z agentami. Ta strona to jej implementacja w Codexie: która powierzchnia Codexa robi które zadanie, dokładne komendy i prompty.
Co zyskujesz dzięki modernizacji z Codexem
Dział zatytułowany „Co zyskujesz dzięki modernizacji z Codexem”- Mapę zadań na powierzchnie: które części migracji idą w CLI, w worktree, w chmurze, a które według harmonogramu.
- Siedem promptów do skopiowania: ocena, testy charakteryzacyjne, infrastruktura migracji, migracja partii, zadanie best-of-N dla trudnego modułu, klasy na hooki i cotygodniowy raport z następną falą.
- Skrypt postępu i zapadkę w CI, które nie pozwalają liczbie plików JavaScript znów rosnąć.
- Listę dowodów, którą tech lead zatwierdza dla każdej fali, oraz sposoby wyjścia z typowych awarii.
Która powierzchnia Codexa robi które zadanie modernizacji?
Dział zatytułowany „Która powierzchnia Codexa robi które zadanie modernizacji?”Dobierz powierzchnię do ryzyka i do tego, ile w zadaniu jest osądu. Komendy poniżej sprawdzono na codex-cli 0.157.1.
| Zadanie | Powierzchnia | Dlaczego tam |
|---|---|---|
| Ocena bazy kodu | codex exec z profilem uprawnień tylko do odczytu | Na tym etapie nic nie powinno się zmienić, a raport ląduje w pliku |
| Testy charakteryzacyjne i infrastruktura migracji | Interaktywny codex na osobnej gałęzi | Współdzielona konfiguracja (tsconfig.json, lint, CI) potrzebuje jednego właściciela i twojej uwagi |
| Partie mechaniczne (moduły pomocnicze, dostęp do danych, trasy) | Jeden worktree Gita na partię, jedna sesja Codexa w każdym | Partie idą równolegle i nie dotykają nawzajem swoich plików |
| Trudne moduły (głębokie callbacki, ukryte zmienne globalne) | codex cloud exec --attempts 2–4 | Istnieje kilka sensownych projektów, więc porównujesz diffy zamiast ufać jednemu |
| Śledzenie postępu i wybór kolejnej partii | Skrypt powłoki plus cotygodniowa automatyzacja Codexa | Liczenie jest deterministyczne; model jest potrzebny tylko do rekomendacji |
GPT-6 Astra, wbudowany domyślny model Codexa, pracuje standardowo z niskim poziomem rozumowania (effort low). Podnieś effort przy ocenie i przy trudnych modułach migrowanych lokalnie, zanim rozważysz inny model: dodaj -c model_reasoning_effort="high" do uruchomienia w CLI, jak w kroku 1, albo wybierz poziom przez /model w sesji interaktywnej. Aktualną listę modeli znajdziesz w przeglądzie modeli.
Krok 1: oceń bazę kodu w trybie tylko do odczytu
Dział zatytułowany „Krok 1: oceń bazę kodu w trybie tylko do odczytu”Uruchom ocenę nieinteraktywnie, żeby nie mogła niczego edytować, a jej raport trafił do pliku, który możesz scommitować. Zapisz poniższy prompt jako assess.md i uruchom w terminalu w katalogu głównym repozytorium:
mkdir -p docs/modernization &&codex exec -c default_permissions=":read-only" -c model_reasoning_effort="high" \ -o docs/modernization/assessment.md - < assess.md-c default_permissions=":read-only" wybiera wbudowany profil uprawnień tylko do odczytu (beta w 0.157.1), -c model_reasoning_effort="high" podnosi effort tylko dla tego uruchomienia, a -o zapisuje ostatnią wiadomość agenta do pliku. mkdir -p tworzy katalog wyjściowy, którego w świeżym repozytorium jeszcze nie ma.
Czytaj ranking ryzyka, nie cały raport. Jeśli moduł, o którym wiesz, że jest kruchy, trafił do „low”, ocena przeoczyła ukryty stan; dopisz go ręcznie do listy wysokiego ryzyka.
Krok 2: utrwal obecne zachowanie, zanim zmieni się jakikolwiek plik
Dział zatytułowany „Krok 2: utrwal obecne zachowanie, zanim zmieni się jakikolwiek plik”Migracja przy słabym pokryciu testami nie ma wyroczni: agent może zmienić zachowanie, a wszystkie sprawdzenia dalej będą zielone. Zanim katalog wejdzie do fali, daj jego publicznemu interfejsowi testy charakteryzacyjne, które zapisują, co kod robi dziś, łącznie z błędami. Technikę niezależną od narzędzia opisuje strona testy charakteryzacyjne dla legacy.
Zanim zaufasz testom, sprawdź, czy potrafią wykryć błąd. Zepsuj ręcznie jedną linię modułu, uruchom ponownie testy charakteryzacyjne i upewnij się, że przynajmniej jeden test jest czerwony; potem cofnij zmianę. Zestaw, który zostaje zielony mimo celowego błędu, nie jest wyrocznią. Scommituj testy na gałęzi bazowej, żeby każda kolejna fala działała na tych samych plikach.
Krok 3: zbuduj infrastrukturę migracji na jednej gałęzi
Dział zatytułowany „Krok 3: zbuduj infrastrukturę migracji na jednej gałęzi”Współdzielona konfiguracja to plik, którego inaczej dotykałaby każda równoległa partia, więc należy do jednej sesji na jednej gałęzi. Uruchom sesję interaktywną na gałęzi w rodzaju ts-migration-infra, daj jej poniższy prompt i scal wynik, zanim ruszy jakakolwiek partia.
Skrypt postępu liczy, a zapadka pilnuje, żeby liczba nie rosła. Oba są deterministyczne, więc żaden nie potrzebuje modelu:
#!/usr/bin/env bash# scripts/migration-progress.sh: JS vs TS files per top-level directory under src/set -euo pipefailprintf '%-28s %6s %6s\n' directory js tsfor d in src/*/; do js=$(find "$d" -type f \( -name '*.js' -o -name '*.jsx' \) | wc -l) ts=$(find "$d" -type f \( -name '*.ts' -o -name '*.tsx' \) | wc -l) printf '%-28s %6d %6d\n' "${d%/}" "$js" "$ts"done# CI step: the ratchet. Lower .migration-baseline on main after a batch merges, never inside a batch PR.js_now=$(find src -type f \( -name '*.js' -o -name '*.jsx' \) | wc -l)test "$js_now" -le "$(cat .migration-baseline)" || { echo "JS file count rose to $js_now"; exit 1; }Krok 4: migruj warstwy niskiego ryzyka w równoległych worktree
Dział zatytułowany „Krok 4: migruj warstwy niskiego ryzyka w równoległych worktree”Weź pierwszą falę z oceny: zwykle najpierw moduły pomocnicze bez wewnętrznych zależności (liście grafu importów), potem dostęp do danych, dalej serwisy, a trasy i UI na końcu. Każda partia dostaje własny worktree i gałąź, żeby dwie sesje nigdy nie edytowały tego samego pliku. Uruchom to w terminalu raz na partię:
git fetch origingit worktree add -b ts/utils ../app-ts-utils origin/maincd ../app-ts-utils && npm ci && codexJeśli nie musisz wybierać nazwy gałęzi, codex --worktree utworzy zarządzany worktree; przewodnik po worktree w Codexie opisuje obie formy i sprzątanie. Wklej ten sam prompt do każdej sesji i zmieniaj tylko katalog:
Scalaj po jednej partii. Po każdym scaleniu obniż .migration-baseline na main do nowej liczby plików JS jednolinijkowym commitem. To, że linia bazowa nie trafia do pull requestów partii, chroni równoległe partie przed kolizją: gdyby każda partia ją obniżała, każde scalenie po pierwszym kończyłoby się konfliktem na tym jednym pliku. Potem przebazuj pozostałe otwarte partie na nową bazę i ponownie przepuść je przez bramki, bo typ dodany w jednej partii potrafi ujawnić błędy w innej.
Krok 5: wyślij trudne moduły do zadań w chmurze z best-of-N
Dział zatytułowany „Krok 5: wyślij trudne moduły do zadań w chmurze z best-of-N”Niektóre moduły mają kilka rozsądnych projektów: sześć poziomów zagnieżdżonych callbacków, hierarchia klas oparta na globalnym obiekcie konfiguracji, zero testów. Dla nich codex cloud exec --attempts N uruchamia do czterech niezależnych prób na tym samym prompcie, a ty porównujesz diffy. Każda próba to pełny przebieg agenta, więc zostaw to dla modułów, w których zły pierwszy projekt drogo kosztuje.
Kontener w chmurze klonuje repozytorium z remote’a, więc infrastruktura z kroku 3 i testy charakteryzacyjne z kroku 2 muszą już być na zdalnym main. Wypchnij go, a potem wskaż gałąź jawnie (w terminalu; ENV_ID to ID lub etykieta twojego środowiska Codex cloud):
git push origin maincodex cloud exec --env ENV_ID --branch main --attempts 3 - < hard-module.mdNazwanie projektów w prompcie nie pozwala próbom zbiec się do tego samego diffa. Gdy zadanie jest gotowe, porównaj próby i zastosuj wybraną w czystym worktree na nowej gałęzi, nie w swojej kopii roboczej, żeby zastosowany diff był od razu gotowy do pull requestu:
codex cloud diff TASK_ID --attempt 2git fetch origingit worktree add -b ts/payments ../verify-payments origin/main && cd ../verify-paymentscodex cloud apply TASK_ID --attempt 2npm ci && npm run typecheck && npm testTASK_ID to ID lub URL zadania, który wypisuje codex cloud exec. Prompt porównujący wszystkie próby obok siebie znajdziesz w sekcji kiedy best-of-N jest wart swojej ceny.
Migracja komponentów klasowych Reacta na hooki
Dział zatytułowany „Migracja komponentów klasowych Reacta na hooki”Przejście z klas na hooki to migracja, w której zachowanie zmienia się najciszej: efekty uruchamiają się po odmalowaniu, a nieaktualne domknięcie czyta stary prop. Traktuj ją jak falę średniego ryzyka z własnym promptem:
Krok 6: śledź postęp i wybieraj kolejną falę według harmonogramu
Dział zatytułowany „Krok 6: śledź postęp i wybieraj kolejną falę według harmonogramu”Uruchamiaj scripts/migration-progress.sh w CI przy każdym scaleniu, żeby liczby pochodziły ze skryptu, a nie z szacunku agenta. Potem niech cotygodniowa automatyzacja Codexa zamieni te liczby w rekomendację. Automatyzacje działają w aplikacji desktopowej Codex, dostępnej na macOS i Windows; na Linuksie zaplanuj codex exec z tym samym promptem w cronie albo w CI. Zanim zaplanujesz automatyzację, uruchom prompt ręcznie dwa razy, tak jak opisuje przewodnik po automatyzacjach. Zadanie dopisuje tabelę do docs/modernization/progress.md, więc uruchamiaj je w worktree i scalaj tę zmianę jak każdą inną gałąź; każde uruchomienie w worktree startuje od świeżej kopii, więc niescalona tabela za tydzień zniknie.
Jak zweryfikować falę modernizacji bez czytania każdej linii?
Dział zatytułowany „Jak zweryfikować falę modernizacji bez czytania każdej linii?”Zmigrowany plik jest poprawny, gdy robi dalej to, co robił. Ustal, co każda fala musi udowodnić, zanim powstanie jakikolwiek diff, i pozwól bramkom to sprawdzić:
-
Wyrocznia się nie ruszyła. Testy charakteryzacyjne i istniejące przechodzą, a fala ich nie edytowała.
git diff --stat origin/main...HEAD -- tests/musi być pusty (albo pokazywać tylko dozwolone zamiany API klasowego). -
Bramki typów i lintera przechodzą w CI, nie tylko w podsumowaniu agenta:
npm run typecheck, linter i pełny zestaw testów. -
Zapadka ruszyła się we właściwą stronę. Liczba plików JS w raporcie agenta spadła, pull request nie dotknął
.migration-baseline, a linię bazową obniżono namainzaraz po scaleniu. -
Automatyczny recenzent przeczytał diff jako pierwszy. W worktree partii najpierw uruchom
git fetch origin, żeby recenzja i sprawdzenietests/z punktu 1 porównywały się z tą samą bazą, a potem wbudowaną recenzję poleceniemcodex exec review --base origin/mainalbo podaj własne instrukcje bez--base:codex exec review "Review the changes on this branch against origin/main. Flag any behavior change: altered error handling, callback-to-promise timing, changed export shapes, or edits to tests."W codex-cli 0.157.1 nie da się połączyć--basez własnymi instrukcjami; polecenie kończy się błędem. Możesz też poprosić o@codex revieww pull requeście. Zobacz code review w Codexie. -
Zatwierdza konkretna osoba. Właściciel partii scala fale niskiego ryzyka na podstawie powyższych dowodów. Właściciel modułu czyta sam kod tylko w klasach zmian, w których ludzie nadal czytają diffy (uwierzytelnianie, pieniądze, schemat i migracje danych), zgodnie z zasadą czytania dowodów zamiast kodu.
-
Produkcja potrafi to cofnąć. Wdrażaj każdą falę osobno, żeby regresja cofała jedną falę, a nie całą migrację.
Co się psuje, gdy Codex modernizuje legacy?
Dział zatytułowany „Co się psuje, gdy Codex modernizuje legacy?”| Objaw | Przyczyna | Jak wyjść |
|---|---|---|
Po scaleniu partii niezmigrowany plik .js kończy się błędem „is not a function” | Partia zamieniła module.exports = fn na eksporty nazwane, a wywołania require() oczekują starego kształtu | Cofnij zmianę eksportu, a potem zachowaj stary kształt, dopóki wszystkie importujące go moduły nie zostaną zmigrowane. Lista importujących plików z raportu partii pokazuje, których plików to dotyczy |
| Testy przechodzą, ale callback odpala się później niż wcześniej | async/await przeniosło pracę z synchronicznego callbacku do mikrozadania | Dodaj test charakteryzacyjny sprawdzający kolejność i powtórz partię |
| Partia jest zielona tylko dlatego, że zmieniono testy | Agent poprawił asercje lub snapshoty pod nowe zachowanie | Sprawdzenie pustego diffa w tests/ blokuje scalenie; powtórz z „fix the migration, not the test” na początku promptu |
Zadanie w chmurze nie widzi tsconfig.json ani nowych testów | Scalona infrastruktura i testy nie zostały wypchnięte na zdalny main albo zadanie ruszyło na innej gałęzi | Najpierw wypchnij i podaj --branch jawnie |
| Wszystkie trzy próby dają ten sam projekt | Prompt zostawił tylko jedną oczywistą drogę | Nazwij alternatywne projekty w prompcie, jak w prompcie dla trudnego modułu |
| Błędy typów pojawiają się w plikach, których nikt nie dotykał | Współdzielony typ albo tsconfig.json zmienił się w ramach partii | Współdzieloną konfigurację trzymaj tylko w gałęzi infrastruktury; partie nie mogą jej edytować |
Pull requesty partii mają konflikt na .migration-baseline | Każda partia sama obniżała linię bazową | Usuń zmianę linii bazowej z partii; obniżaj ją na main po każdym scaleniu, a gdy dwa scalenia się ścigają, weź niższą liczbę |
| Dwie partie mają konflikt przy scalaniu | Ich katalogi współdzielą moduł albo obie edytowały plik indeksu | Przebazuj późniejszą partię po scaleniu pierwszej; w planie fali przypisz współdzielone pliki indeksu jednej partii |
| Cotygodniowy raport powtarza rekomendację z zeszłego tygodnia | Automatyzacja nie pamięta, co już się wydarzyło | Trzymaj jej stan w docs/modernization/progress.md i każ promptowi porównywać się z tym plikiem |
Dokąd dalej z modernizacją legacy
Dział zatytułowany „Dokąd dalej z modernizacją legacy”Ta sama migracja w innych narzędziach: refaktoryzacja legacy w Cursorze i migracje z Claude Code.