Każde demo AI wygląda tak samo. Nowy projekt, czysty kod, 200 linii, zero historii. Prompt, enter - działa. A potem przychodzi poniedziałek i otwierasz serwis, którego nikt nie ruszał od 2 lat. Nie dlatego, że działa idealnie, tylko dlatego, że wszyscy się go boją.
Ja miałem taki serwis: ponad 500 linii, 1 metoda na 180 linii, zero testów, a na górze komentarz "NIE RUSZAĆ". Gdybym wkleił ten plik do AI i napisał "zrefaktoruj", dostałbym piękny kod. I zepsutą produkcję. Mimo to zrefaktorowałem ten serwis z AI w jeden weekend - i nic nie wybuchło.
W tym artykule pokazuję dokładnie, jak to zrobiłem: jaki prompt daję na start, jak "zamrażam" stary kod testami charakteryzacyjnymi, jak dzielę pracę na małe kroki i czego w legacy nigdy nie oddaję AI. Zobaczysz też 4 "poprawki" AI, z których każda wygląda niewinnie, metodę, której "nikt nie woła" (a której usunięcie wyszłoby na jaw dopiero 31 grudnia), i liczby przed i po.
Wersje .NET: przykłady są w C# z testami w xUnit. TimeProvider, którego używam do odcięcia testów od zegara systemowego, jest wbudowany w .NET 8 i nowsze, a w starszych projektach (także na .NET Framework) dodasz go pakietem Microsoft.Bcl.TimeProvider. Sama metoda - mapa, testy, małe kroki - nie zależy ani od wersji frameworka, ani od narzędzia AI. Ja pracowałem w Claude Code, ale tak samo zastosujesz ją z innym asystentem.
Demo AI kontra prawdziwy projekt: poznaj InvoiceService
Demo nie ma historii. Twój projekt ma 10 lat historii, 0 testów, ponad 500 linii w jednym pliku i komentarz "NIE RUSZAĆ" - a nikt nie pamięta połowy decyzji, które do tego doprowadziły.
Bohaterem tego artykułu jest InvoiceService.cs: 512 linii w jednym pliku, ostatnia zmiana 2 lata temu, 0 testów, metoda Process() na 180 linii i komentarz "NIE RUSZAĆ!!!", którego autor dawno nie pracuje w firmie. Fragment wygląda tak:
public void Process(int orderId)
{
var order = _repo.Get(orderId);
if (order != null)
{
if (order.Status != 7)
{
if (order.IsVip && DateTime.Now.Month == 12)
{
using var conn = new SqlConnection(_cs);
/* ... 40 linii SQL-a ... */
}
try { total = net + net * 0.23m; Send(order); }
catch { }
}
}
}Pewnie znasz ten zapach. W kilkunastu linijkach mamy 5 problemów naraz:
1. Kilka poziomów zagnieżdżonych if-ów.
2. DateTime.Now w środku logiki biznesowej.
3. new SqlConnection w metodzie, która liczy rabat.
4. 23% VAT-u wpisane z ręki jako 0.23m.
5. Pusty catch, który połyka każdy błąd.
Kto tego używa? Najgorsze nie jest jednak to, co jest w pliku, tylko to, kto go używa. W moim przypadku: 3 endpointy API, nocny job uruchamiany o 02:00, raport w SQL-u, który czyta te same tabele... i pewnie coś, o czym nikt nie wie. Każda zmiana w tym serwisie może zepsuć rzecz, której nie widać z poziomu pliku.
Plan jest więc taki: najpierw zobaczymy, dlaczego AI w legacy "głupieje". Potem przejdziemy przez 4 kroki, dzięki którym przestaje: mapa, testy charakteryzacyjne, małe kroki i granice tego, co oddajesz AI. Na końcu liczby przed i po.
Dlaczego AI "głupieje" w legacy kodzie?
Typowy refaktor z AI wygląda tak: wklejasz 500 linii, piszesz "zrefaktoruj to" i dostajesz diff na 600 linii. Kompiluje się, testów nie ma, więc wszystko wygląda dobrze. "LGTM", merge. A w piątek o 16:00 dzwoni telefon, bo produkcja stoi w ogniu.
Najgorsze jest to, że w takim diffie nie ma oczywistych błędów. Są za to zmiany, które wyglądają jak dobre praktyki. Oto 4 przykłady.
"Poprawka" 1: DateTime.Now na DateTime.UtcNow. AI mówi: "czas zawsze trzymaj w UTC". Brzmi rozsądnie:
/* wystawienie faktury - przed */
invoice.IssueDate = DateTime.Now;
/* po "poprawce" */
invoice.IssueDate = DateTime.UtcNow;Tylko że latem Polska to UTC+2. Faktura wystawiona 1 lipca o 01:00 dostaje datę 30 czerwca, czyli trafia do poprzedniego miesiąca. Na przełomie miesiąca to już nie kosmetyka, tylko inny okres rozliczeniowy.
"Poprawka" 2: Math.Round z AwayFromZero. AI mówi: "pieniądze zaokrągla się od zera":
/* przed */
var gross = Math.Round(amount, 2);
/* po "poprawce" */
var gross = Math.Round(amount, 2, MidpointRounding.AwayFromZero);Math.Round w .NET domyślnie zaokrągla bankowo, czyli do parzystej. Kwota 2,345 dawała więc 2,34, a po zmianie daje 2,35. Grosz różnicy - ale na tysiącach faktur. I księgowość dzwoni. Uczciwie trzeba przyznać, że co do zasady AI może tu nawet mieć rację. Tyle że to decyzja księgowości, a nie punkt refaktoru.
"Poprawka" 3: usunięty pusty catch. AI mówi: "pusty catch połyka błędy, usuwam". Wiadomo, zła praktyka:
/* przed */
foreach (var order in orders)
{
try { Process(order); }
catch { }
}
/* po "poprawce" */
foreach (var order in orders)
{
Process(order);
}Przez 2 lata nocny job po cichu pomijał zepsute rekordy. Teraz wywala się na pierwszym z nich, a reszta faktur po prostu się nie generuje.
"Poprawka" 4: ToString z InvariantCulture. AI mówi: "format nie może zależeć od kultury":
/* eksport CSV dla księgowości - przed */
csv.Append(total.ToString());
/* po "poprawce" */
csv.Append(total.ToString(CultureInfo.InvariantCulture));Ładnie i przewidywalnie. Tyle że w pliku CSV dla księgowości zamiast "1234,50" jest teraz "1234.50", a Excel księgowej czyta kwoty jako tekst albo jako daty.
Każda z tych zmian wygląda jak poprawka. Każda zmienia zachowanie. A w legacy zachowanie to kontrakt - nawet jeśli nikt go nigdy nie spisał.
4 powody, dla których tak się dzieje:
1. AI widzi plik, ale nie widzi, kto go woła: joba, refleksji, raportu w SQL-u.
2. Nie wie, że jakiś bug to feature, na którym ktoś polega od 2 lat.
3. Uczy się z kodu, który widzi. Jak widzi złe wzorce, dopisuje kolejne w tym samym stylu.
4. Bez testów nie ma żadnej kontroli. "Działa" znaczy wtedy po prostu "kompiluje się".
Dlatego AI wcale nie głupieje. W legacy najważniejsza wiedza po prostu nie leży w kodzie. Twoja robota to wyciągnąć tę wiedzę, zanim zmienisz pierwszą linię. I to jest krok pierwszy.
Krok 1: najpierw mapa, potem zmiany
Zasada 2 godzin. Moja zasada brzmi: przez pierwsze 2 godziny AI nie zmienia ani jednej linii. Tylko czyta i mapuje.
Prompt na mapę. Tak wygląda prompt, od którego zaczynam:
Przeanalizuj InvoiceService.cs. NIE zmieniaj żadnego pliku.
Stwórz LEGACY_MAP.md, w którym wypiszesz:
1. Wejścia: kto i skąd woła każdą publiczną metodę
2. Zależności: baza, pliki, HTTP, mail, konfiguracja, czas
3. Efekty uboczne: zapisy do bazy, maile, pliki
4. Ryzyka: stan statyczny, DateTime.Now, catch { }, magiczne liczby
5. Pytania: czego nie da się wyczytać z koduZwróć uwagę na "NIE zmieniaj żadnego pliku" wielkimi literami. AI zachowuje się tu jak junior, który bardzo chętnie "pomoże". W Claude Code możesz dodatkowo uruchomić to w trybie planowania - wtedy nic się nie zmieni nawet przez przypadek. Mamy 5 rzeczy do znalezienia, a punkt 5, czyli lista pytań, to najcenniejsza część całej mapy. Zaraz do niej wrócę.
Mapa po kwadransie. Tak wyglądał fragment mojej mapy po 15 minutach:
# LEGACY_MAP.md: InvoiceService
## Wejścia (4)
- InvoicesController: Create, Get, Correct
- NightlyInvoiceJob (codziennie 02:00)
## Ukryte zależności
- DateTime.Now × 6
- SqlConnection w Process() i CalculateTotal()
- SMTP wysyłany w środku Process()
## Efekty uboczne
- INSERT do Invoices i InvoiceLines
- PDF zapisywany na \\fs01\faktury
## Pytania
- Co znaczy Status == 7?4 wejścia, w tym nocny job. 6 razy DateTime.Now. Mail do klienta wysyłany ze środka metody, która liczy. I status 7, o którym nikt nie wiedział, co znaczy.
git log to czarna skrzynka legacy. Druga rzecz to historia pliku. Proszę AI:
Przejrzyj historię InvoiceService.cs
(git log --follow -p).
Wypisz hotfixy: kiedy, co naprawiały,
której metody dotyczyły.U mnie w ostatnich 2 latach było 7 hotfixów, z czego 5 dotyczyło jednej metody: CalculateTotal(). Po 1 trafiło w Process() i SendMail(). Od razu wiesz, gdzie leżą miny i gdzie testy muszą być najgęstsze.
Czego mapa AI nie widzi. Uwaga: AI zmapuje to, co jest w solucji. Resztę musisz sprawdzić sam:
• refleksja i nazwy metod zapisane w stringach,
• rejestracje w DI po konwencji,
• joby i crony skonfigurowane poza kodem,
• procedury składowane i raporty w SQL-u,
• inne serwisy, które czytają te same tabele,
• Excel księgowej, który importuje Twój CSV.
Find All References tego nie znajdzie. Szukaj nazwy metody jako tekstu - w całym repo i w bazie:
git grep -n "CalculateTotal"Z listą pytań idziesz do ludzi. Wracamy do punktu 5. AI wypisze Ci rzeczy, których nie da się wyczytać z kodu, na przykład:
• Dlaczego rabat VIP liczy się przed VAT-em, a zwykły po?
• Co znaczy Status == 7?
• Kto czyta pliki z \\fs01\faktury?
Z tą listą idziesz do ludzi. 15 minut z kimś z biznesu jest wartych więcej niż 3 godziny zgadywania.
Krok 2: testy charakteryzacyjne, czyli siatka bezpieczeństwa
Masz mapę. Teraz czas na siatkę bezpieczeństwa.
Czym są testy charakteryzacyjne? To pojęcie z książki Michaela Feathersa o pracy z legacy ("Working Effectively with Legacy Code"). Różnica jest prosta:
• test jednostkowy sprawdza, czy kod działa dobrze,
• test charakteryzacyjny sprawdza, czy kod działa tak samo jak wczoraj.
Zamrażasz obecne zachowanie. Razem z bugami.
Jak to się robi? W 4 krokach:
1. Wywołujesz metodę z konkretnymi danymi.
2. Patrzysz, co zwraca.
3. Wpisujesz to jako oczekiwany wynik.
4. Test od razu jest zielony.
Jego zadaniem nie jest sprawdzenie poprawności, tylko zrobienie się czerwonym, kiedy coś zmienisz przez przypadek. W xUnit wygląda to tak:
[Fact]
public void CalculateTotal_VipWithThreeLines_ReturnsCurrentValue()
{
var order = TestOrders.Vip(lines: 3, net: 1_250.00m);
var total = _sut.CalculateTotal(order);
/* Wartość z obecnego kodu, NIE z wymagań! */
Assert.Equal(1_476.38m, total);
}Zwróć uwagę na komentarz. Ta liczba nie pochodzi z wymagań. Pochodzi z uruchomienia starego kodu.
Szew: TimeProvider zamiast DateTime.Now. Jest jeden problem: takiego kodu często nie da się przetestować, bo w środku siedzą DateTime.Now i new SqlConnection. Potrzebujesz szwu, czyli najmniejszej zmiany, która pozwala podmienić zależność w teście. Dla czasu w .NET 8 masz TimeProvider:
/* przed */
if (DateTime.Now.Month == 12)
ApplyYearEndBonus(order);
/* po: TimeProvider time wstrzyknięty w konstruktorze */
if (_time.GetLocalNow().Month == 12)
ApplyYearEndBonus(order);A w teście podstawiasz FakeTimeProvider z pakietu Microsoft.Extensions.TimeProvider.Testing i ustawiasz dowolny moment, np. sylwestra, minutę przed północą:
var time = new FakeTimeProvider(
new DateTimeOffset(2025, 12, 31, 23, 59, 0, TimeSpan.FromHours(1)));
time.SetLocalTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Europe/Warsaw"));Uwaga: GetLocalNow(), nie GetUtcNow(). Inaczej sam zrobisz "poprawkę" nr 1 z początku artykułu. Z SQL-em i mailem postępujesz tak samo: wyciągasz interfejs. Najlepiej automatycznym refaktorem w IDE, a nie ręcznie.
Duży obiekt? Snapshot zamiast 30 asercji. Jeśli metoda zwraca duży obiekt, np. całą fakturę z pozycjami, nie pisz 30 asercji. Użyj snapshotów, np. z biblioteką Verify (pakiet Verify.Xunit). Test ma wtedy 1 linijkę logiki:
[Fact]
public Task BuildInvoice_VipOrder()
{
var inv = _sut.BuildInvoice(TestOrders.Vip());
return Verify(inv);
}Działa to w 3 krokach:
1. Pierwszy przebieg zapisuje wynik do pliku .received.txt.
2. Ty go przeglądasz i zatwierdzasz - powstaje plik .verified.txt.
3. Każdy kolejny przebieg porównuje wynik z zatwierdzonym. Różnica = czerwony test.
Plik .verified.txt commitujesz razem z testem.
AI wymyśla przypadki, kod podaje wyniki. Tu AI wykonuje sporo roboty, ale obowiązuje jedna zasada, którą wpisuję prosto do promptu:
Wypisz przypadki brzegowe dla CalculateTotal():
granice progów, puste dane, daty, waluty, zaokrąglenia.
Napisz dla nich testy charakteryzacyjne.
Oczekiwane wartości weź WYŁĄCZNIE z uruchomienia obecnego kodu.
Nie licz ich sam. Nie poprawiaj kodu.Jeśli pozwolisz AI zgadywać oczekiwane wartości, napisze testy do tego, jak kod powinien działać. A Ty masz zamrozić to, jak działa naprawdę.
Przykładowa lista przypadków brzegowych, którą dostałem:
• pusta lista pozycji,
• ilość równa 0,
• rabat 100%,
• kwota z końcówką 5 tysięcznych (0,005),
• sylwester, 23:59,
• klient bez NIP-u (NIP = null),
• faktura w obcej walucie (EUR),
• zamówienie tuż pod i tuż nad progiem rabatu (±0,01).
Każdy z nich to potencjalna mina z historii gita.
Czy siatka ma dziury? Skąd wiesz, że testów jest dosyć? Masz 2 sprawdziany.
• Pokrycie gałęzi, ale tylko w metodach, które zmieniasz, a nie w całym systemie.
• Test mutacyjny. Celowo psujesz kod i patrzysz, czy coś się zaczerwieni. Stryker.NET robi to automatycznie.
Pokrycie zbierzesz tak:
dotnet test --collect:"XPlat Code Coverage"a Stryker.NET instalujesz jako narzędzie dotnet i uruchamiasz w folderze projektu z testami:
dotnet tool install -g dotnet-stryker
dotnet strykerJest też wersja ręczna, na 30 sekund: zmień "większe" na "większe lub równe" przy progu rabatu.
/* przed */
if (net > 10_000m)
/* celowo zepsute */
if (net >= 10_000m)Jeśli wszystko dalej jest zielone, siatka ma dziurę.
Test pokazał bug. Co teraz? Prędzej czy później test pokaże Ci bug. U mnie rabat VIP naliczał się 2 razy powyżej 10 000 zł. Co robisz? Nic. Test zostaje z obecnym, złym wynikiem i komentarzem:
[Fact]
public void CalculateTotal_VipAbove10k_CurrentBehavior()
{
/* BUG? Rabat VIP naliczany 2x powyżej 10 000 zł.
BUGS_FOUND.md #3. NIE poprawiać w ramach refaktoru. */
var total = _sut.CalculateTotal(TestOrders.Vip(net: 12_000m));
Assert.Equal(13_602.82m, total);
}Potem 3 kroki:
1. Zostaw obecny wynik w teście.
2. Wpisz problem do BUGS_FOUND.md i zgłoś go.
3. Napraw osobno, po refaktorze, kiedy biznes zdecyduje. Bo może ktoś na tym bugu polega.
Refaktor nie naprawia bugów. Refaktor nie zmienia zachowania. Kropka. Mieszanie jednego z drugim to najczęstszy powód, dla którego refaktory kończą się rollbackiem.
Krok 3: małe kroki w pętli
Dopiero teraz zmieniasz kod.
Pętla na cały weekend. Pracujesz w pętli: 1 zmiana → dotnet build → dotnet test. Zielone? git commit. Czerwone? Nie debugujesz. Robisz git restore . i próbujesz mniejszego kroku.
CLAUDE.md: tryb refaktoru. Zanim ruszysz, dopisz do CLAUDE.md sekcję trybu refaktoru. To brief dla juniora na cały weekend - wklejasz raz, a działa przy każdym kroku:
## Tryb refaktoru: InvoiceService
- NIE zmieniaj zachowania. Nawet jeśli coś wygląda na bug.
- Jedna zmiana na raz. Po każdej: dotnet build + dotnet test.
- NIE ruszaj publicznych sygnatur, DTO ani nazw kolumn.
- NIE aktualizuj pakietów "przy okazji".
- NIE usuwaj kodu, który wygląda na martwy.
- Podejrzane rzeczy dopisuj do BUGS_FOUND.md zamiast poprawiać.Jeśli pracujesz z innym asystentem niż Claude Code, wklej te same zasady do jego pliku z instrukcjami projektu.
Prompt na jeden krok. Prompt na pojedynczy krok jest krótki i bardzo konkretny:
Wydziel z Process() liczenie rabatu do prywatnej metody
CalculateDiscount(Order order). Tylko to.
Nie zmieniaj nazw, formatowania ani logiki.
Uruchom dotnet test i pokaż mi diff.Ma 3 cechy: dotyczy jednej rzeczy, wprost mówi, czego nie ruszać, i każe AI samemu odpalić testy i pokazać diff.
Review diffa jak u juniora. Moje 4 zasady review:
• Diff do 50 linii, czyli coś, co przeczytasz w 2 minuty.
• Coś ekstra? Odrzucam całość. Nawet jeśli AI "tylko" przeformatowało plik. Nie wybieram kawałków.
• Czerwone testy? Cofam. Nie naprawiam na szybko.
• 2 razy czerwone z rzędu? Dzielę krok na pół. Był za duży.
Kolejność ma znaczenie. Refaktor robię w 3 etapach:
1. Ruchy mechaniczne: nazwy, wydzielanie metod, stałe zamiast magicznych liczb.
2. Szwy: TimeProvider, interfejsy dla bazy i maila.
3. Struktura: nowe klasy i podział odpowiedzialności - dopiero na końcu.
I jedna rada: Rename i Extract Method rób w IDE. To refaktory automatyczne, bezpieczne i darmowe. AI zostaw to, czego IDE nie umie.
Mój weekend w historii gita. Tak to wyglądało u mnie:
$ git log --oneline --reverse
a1f3c2e test: testy charakteryzacyjne dla CalculateTotal()
b7d9e01 test: snapshoty Verify dla BuildInvoice()
c2e8f44 refactor: stałe zamiast magicznych liczb (VAT, progi)
d91a7b3 refactor: wydziel CalculateDiscount()
e4c0d12 refactor: TimeProvider zamiast DateTime.Now
f8b2a6c refactor: IInvoiceRepository zamiast SqlConnection
0c7e5d9 refactor: IInvoiceMailer zamiast SMTP w Process()
9a3b1f7 refactor: wydziel InvoiceCalculator
... (+26 kolejnych)34 commity. Każdy mały, każdy z zielonymi testami, każdy do cofnięcia jednym poleceniem. A jeśli w poniedziałek coś pójdzie źle, git bisect w minutę pokaże, który krok to zrobił.
Krok 4: czego w legacy nie oddajesz AI
Czwarty krok to granice. Podział pracy wygląda u mnie tak:
| AI wykonuje | Ty decydujesz |
| Mapa zależności | Co jest bugiem, a co funkcją |
| Analiza historii gita | Każda zmiana zachowania: zaokrąglenia, strefy czasowe, formaty |
| Lista pytań do biznesu | Publiczne kontrakty: API, DTO, JSON, nazwy kolumn |
| Przypadki testowe | Baza i migracje |
| Testy z wartościami z uruchomienia | Usuwanie kodu |
| Refaktory mechaniczne | Merge i deploy |
| Dokumentacja |
Uwaga na "modernizację przy okazji". Osobna kategoria ryzyka to zmiany, które wyglądają jak kosmetyka:
• Newtonsoft.Json → System.Text.Json. System.Text.Json z domyślnymi opcjami rozróżnia wielkość liter w nazwach pól i nie wczyta liczby zapisanej w cudzysłowie - rzuci wyjątek. Newtonsoft robił jedno i drugie bez słowa. Pułapka jest tym większa, że ASP.NET Core ustawia dla swoich endpointów opcje "webowe", w których obie te rzeczy są włączone, więc różnica wychodzi dopiero przy ręcznym wywołaniu JsonSerializer.
• Nowa wersja EF Core. Te same zapytania LINQ potrafią wygenerować inny SQL, a czasem wyjątek tam, gdzie wcześniej wszystko działało.
• .NET Framework → nowy .NET. To osobny projekt, a nie punkt refaktoru.
Zasada: upgrade = osobny PR. Nigdy "przy okazji".
Metoda, której nikt nie woła. A teraz obiecana historia. Metoda CloseFiscalYear(). Find All References: 0. AI pyta: "martwy kod, usuwam?". Brzmi rozsądnie... gdyby nie job, który woła ją przez refleksję, po nazwie zapisanej w bazie:
/* YearEndJob.cs */
var method = typeof(InvoiceService).GetMethod(task.MethodName); /* nazwa z bazy */
method.Invoke(service, new object[] { DateTime.Now.Year });Raz w roku, 31 grudnia, przy zamknięciu roku. Usuwasz w październiku, dowiadujesz się w styczniu. Dlatego martwego kodu nie usuwasz bez dowodu: logi, kilka tygodni telemetrii, pytanie do biznesu. A w trakcie refaktoru najlepiej wcale.
Bonus: wdrożenie bez stresu (shadow run)
Refaktor gotowy, testy zielone. Jak to wypuścić bez stresu? Nie przełączasz od razu. Stary kod dalej liczy i zwraca wynik, a nowy liczy obok i tylko loguje różnice:
/* InvoiceFacade.cs */
var oldTotal = _legacy.CalculateTotal(order);
var newTotal = _calculator.CalculateTotal(order);
if (oldTotal != newTotal)
_log.LogWarning("Różnica {Id}: {Old} vs {New}",
order.Id, oldTotal, newTotal);
return oldTotal; /* przez tydzień: STARY wynik */Przez tydzień porównujesz. Zero różnic? Przełączasz. Są różnice? Masz je w logach, a nie w reklamacjach.
Tylko uwaga: to działa dla obliczeń. Nie dla kodu, który wysyła maile albo zapisuje do bazy - bo klient dostanie 2 maile.
Przed i po: liczby z jednego weekendu
A teraz obiecane liczby dla InvoiceService:
| Metryka | Przed | Po |
| Największa klasa | 512 linii | 140 linii |
| Najdłuższa metoda | 180 linii | 24 linie |
| Złożoność cyklomatyczna | 41 | 7 |
| Testy | 0 | 43 |
Złożoność cyklomatyczna to liczba ścieżek przez kod. W Visual Studio policzysz ją przez Analyze → Calculate Code Metrics. Do tego: 34 commity, każdy z zielonymi testami, ok. 13 godzin pracy w sobotę i niedzielę, 3 znalezione bugi zgłoszone osobno i 0 incydentów po wdrożeniu - zero rollbacków.
Najważniejsza liczba. Ale najważniejsza liczba jest inna. Następna zmiana w tym serwisie zajęła 40 minut. Wcześniej nikt nie chciał jej nawet wycenić. To jest prawdziwy zysk z refaktoru: legacy przestało być legacy.
Podsumowanie
AI w legacy nie musi kończyć się telefonem w piątek o 16:00. Wystarczy, że zamiast "zrefaktoruj to" dasz mu proces. 5 zasad do zabrania:
• Mapa przed zmianą: przez pierwsze 2 godziny AI tylko czyta i mapuje.
• Zamroź zachowanie testami charakteryzacyjnymi: AI wymyśla przypadki, kod podaje wyniki.
• 1 zmiana = 1 commit = zielone testy: czerwone? Cofasz, nie debugujesz.
• Refaktor nie naprawia bugów: znaleziony bug trafia do BUGS_FOUND.md, a nie do diffa.
• Decyzje zostają u Ciebie, AI wykonuje: zachowanie, kontrakty, baza, usuwanie kodu, merge i deploy.
Zadanie na poniedziałek. Otwórz najstraszniejszy plik w swoim projekcie. Nie zmieniaj go. Poproś AI tylko o mapę, promptem z kroku 1. Założę się, że znajdziesz przynajmniej jedną rzecz, o której nie wiedziałeś.
To, co tu opisałem, to szkielet. Jeśli chcesz przejść tę drogę od początku do końca, zapraszam Cię do Szkoły 3x Dev - szkolenia dla programistów C# i .NET, w którym krok po kroku uczę pracy z AI. W module 6 "Legacy serwis w weekend" robimy dokładnie to, co w tym artykule, ale na prawdziwym, brzydkim serwisie: mapa, testy, refaktor i wdrożenie. A w module 5 uczysz się debugować z AI, kiedy coś jednak pójdzie nie tak.
Zapisy otwieram cyklicznie, więc wejdź na stronę i zapisz się, a jeśli akurat są zamknięte, zostaw maila na liście oczekujących: modestprogrammer.pl/szkola-3x-dev.
Powodzenia z Twoim najstraszniejszym plikiem :)