Refaktor o 3 w nocy, o który nikt nie prosił
Refaktor o 3 w nocy, o który nikt nie prosił
Zaczęło się o 21:00 we wtorek. “Szybki cleanup” przed snem.
Serwis obsługiwał dostarczanie webhooków. Działał. Testy przechodziły. Użytkownicy byli zadowoleni. Ale wpatrywałem się w niego wcześniej podczas code review i jedna funkcja mnie irytowała. Nie zepsuta — po prostu brzydka. Zagnieżdżony warunek, który mógłby być guard clause. Pięć minut, góra.
Otworzyłem plik. Zaznaczyłem funkcję. Poprosiłem Claude’a, żeby ją oczyścił.
Sugestia była dobra. Guard clause, early return, warunek spłaszczony w coś czytelnego. Zaakceptowałem. Potem zauważyłem, że funkcja poniżej używa podobnego wzorca. Mogę to też naprawić.
Claude się zgodził. Zwrócił też uwagę, że obsługa błędów w obu funkcjach jest niespójna. Niektóre funkcje rzucały wyjątki, inne zwracały obiekty błędów. “Chcesz, żebym ustandaryzował wzorzec obsługi błędów w tym pliku?”
Tak. Oczywiście, że tak. Spójność ma znaczenie.
To było 21:20.
Kaskada
Do 22:00 dotknąłem trzech plików. Refaktor obsługi błędów w pierwszym pliku oznaczał, że kod wywołujący w drugim pliku musiał się zmienić. Drugi plik importował utility z trzeciego pliku, który używał starego wzorca. Każda poprawka była prawidłowa. Każda poprawka ujawniała następną poprawkę.
Do 23:00 plik utility był zreorganizowany. Funkcje pogrupowane według odpowiedzialności. Martwe eksporty usunięte. Typy doprecyzowane. Claude znalazł dwie funkcje, które robiły prawie to samo, i zaproponował ich scalenie. Merge był czysty. Testy nadal przechodziły. Czułem się dobrze.
O północy byłem w warstwie bazy danych. Zrefaktorowany utility używał query buildera, który miał swoje niespójności. Query builder działał. Działał od ośmiu miesięcy. Ale teraz, gdy warstwa powyżej była czysta, warstwa poniżej wyglądała niechlujnie w porównaniu.
“Ten query builder mógłby skorzystać z bardziej komponowalnego API” — zaproponował Claude. “Chcesz zobaczyć wzorzec z method chainingiem?”
Chciałem zobaczyć. Oczywiście, że chciałem.
Koszty utopione w kodzie
Jest coś takiego w byciu cztery pliki w głąb refaktoru o północy: odejście wydaje się marnotrawstwem.
Już zainwestowałeś trzy godziny. Pierwsze trzy pliki są czystsze. Jeśli teraz przestaniesz, masz codebase w połowie zrefaktorowany — czysty na górze, bałagan na dole. To czuje się gorzej niż punkt wyjścia. Więc kontynuujesz. Nie dlatego, że praca jest ważna, ale dlatego, że zatrzymanie się “zmarnowałoby” to, co już zrobiłeś.
To podręcznikowa pułapka kosztów utopionych. Te trzy godziny przepadły niezależnie od tego, co zrobisz. Kod działał wcześniej. Działa teraz. Jedyne, co się zmieniło, to formatowanie. Ale twój mózg ramkuje to inaczej: te trzy godziny to inwestycja, a inwestycje muszą się zwrócić. Zwrot to ukończenie. Więc wchodzisz w piąty plik.
Piąty plik jest największy. Ma własne problemy, własne wzorce, własny dług. Claude znajduje dwanaście rzeczy do poprawy. Każda jest uzasadniona. Każda zajmuje dziesięć minut. Robisz rachunek — dwie godziny — i myślisz: skoro już tu jestem, co to są jeszcze dwie godziny, kiedy już spędziłem trzy?
Wszystko. Dwie godziny to wszystko. To różnica między sześcioma a czterema godzinami snu. Między ostrym myśleniem rano a wleczeniem się przez poranek z kawą jako osobowością.
Ale AI nie wie o twoim poranku. Nie modeluje twojego snu. Nie uwzględnia malejących zwrotów z refaktorowania kodu, który działa i shipuje. Widzi kod. Widzi ulepszenia. Sugeruje je. Do tego jest zbudowane.
Dark flow
Jest taka koncepcja w psychologii behawioralnej — “dark flow”. Normalny flow state ma jasny cel i naturalny punkt zakończenia. Budujesz feature, wchodzisz w flow, feature jest gotowy, przestajesz. Dark flow nie ma punktu końcowego. Jesteś pochłonięty, czujesz się produktywny, czas znika — ale żaden cel nie jest realizowany. Tylko aktywność, która przypomina produktywność.
Różnica jest subtelna od wewnątrz. Oba stany czują się tak samo: skupiony, kompetentny, zaangażowany. Rozwiązujesz problemy. Każde rozwiązanie jest satysfakcjonujące. Przerwa między pytaniem a odpowiedzią jest minimalna — pytasz Claude’a, odpowiada w sekundy, oceniasz, akceptujesz lub poprawiasz, idziesz dalej. Pętla jest szybka. Feedback natychmiastowy. Wzmocnienie o zmiennym stosunku — czasem sugestia jest idealna, czasem wymaga tweakowania, czasem zaskakuje — trzyma twoją uwagę na miejscu.
To mechanizm automatu do gier ubrany w edytor kodu.
Prawdziwy flow służy miejscu docelowemu. Wiesz, kiedy skończyłeś, bo zdefiniowałeś “skończone” zanim zacząłeś. Dark flow nie ma definicji “skończone”. Zawsze jest następny plik. Kolejna niespójność. Kolejny wzorzec, który mógłby być “bardziej idiomatyczny”. AI jest nieskończenie cierpliwe i nieskończenie produktywne. Nigdy nie powie “to wystarczy”. Nigdy nie powie “idź spać”. Nigdy nie spojrzy na zegarek i nie skrzywi się.
To twoja robota. A o 2 w nocy, po pięciu godzinach stabilnych mikro-ulepszeń, straciłeś zdolność wykonywania tej roboty. Flow state przejął twoją funkcję wykonawczą. Nie decydujesz się kontynuować. Kontynuujesz, bo zatrzymanie wymaga decyzji, a decyzje wymagają kory przedczołowej, a twoja kora przedczołowa wybiła się dwie godziny temu.
Poranek po
Skończyłem o 3 w nocy. Nie dlatego, że tak zdecydowałem. Bo build się wysypał na niezwiązanej zależności i urok prysnął.
Pushnąłem brancha i poszedłem spać.
Następnego ranka otworzyłem pull requesta. Diff: 847 zmienionych linii w 9 plikach. Przeglądałem z kawą, próbując przypomnieć sobie, dlaczego każda zmiana miała znaczenie. Niektóre były prawdziwymi usprawnieniami. Większość była ruchami bocznymi — innymi, nie lepszymi. Kilka było prawdopodobnie gorszych — dodawały abstrakcję tam, gdzie nie była potrzebna.
Funkcjonalność była identyczna. Każdy test, który przechodził wcześniej, nadal przechodził. Każdy test, który nie przechodził wcześniej, nadal nie przechodził. Zero naprawionych bugów. Zero dodanych feature’ów. Zero poprawy wydajności.
Spędziłem sześć godzin na upiększaniu kodu dla publiczności złożonej z zera. Serwis działał na serwerze. Nikt go nie czytał. Nikt nigdy go nie przeczyta. Byłem jedynym programistą na tym projekcie.
Zamknąłem PR-a. Zrevertowałem brancha.
Cztery godziny snu — poszły. Poranek ostrego myślenia — poszedł. Za nic. Za mniej niż nic — bo teraz miałem też ten specyficzny wstyd wiedzy, że zrobiłem to sobie sam, znowu, i zrobię to znowu, jeśli nie będę uważał.
Jak odróżnić jedno od drugiego
Mam teraz trzy pytania przyklejone do monitora. Kiedy czuję ciągnienie “jeszcze jedno ulepszenie”, zatrzymuję się i odpowiadam na nie:
Czy potrafię sformułować swój cel w jednym zdaniu? Nie “posprzątać kod” — to nie jest cel, to kierunek. Cel to: “Wyekstrahować logikę retry do wspólnego utility, żeby nowy endpoint mógł z niej korzystać.” Jeśli nie potrafię nazwać konkretnego, ograniczonego rezultatu, dryfuję.
Czy zatrzymanie się teraz spowoduje utratę znaczącego postępu? Znaczący oznacza: ktoś poza mną to zauważy. Feature działa, który nie działał wcześniej. Bug jest naprawiony, który dotyczył użytkowników. Deployment, który był zablokowany, został odblokowany. “Kod jest ładniejszy” to nie znaczący postęp. To pielęgnacja.
Czy ktoś poprosił o tę pracę? Nie “czy ktoś by to zaaprobował” — czy ktoś konkretnie o to poprosił? Czy jest na boardzie? W tickecie? W rozmowie? Jeśli jestem jedyną osobą, która wie, że ta praca się dzieje, to sygnał. Ważna praca ma interesariuszy. Nocne refaktory mają publiczność złożoną z jednego.
Jeśli wszystkie trzy odpowiedzi to “nie” — brak jasnego celu, żaden znaczący postęp nie jest zagrożony, nikt o to nie prosił — zamykam laptopa. Nie za pięć minut. Teraz. Bo “jeszcze pięć minut” to narkotyk na wejście, i wiem dokładnie, dokąd prowadzi.
Niewygodna część
Kod był w porządku. Zawsze był w porządku. AI nie oszukało mnie, żebym go refaktorował. To ja zdecydowałem się zacząć. To ja zdecydowałem się kontynuować. AI po prostu sprawiło, że każdy pojedynczy krok był na tyle bezwysiłkowy, że nigdy nie miałem naturalnego punktu zatrzymania.
To jest mechanizm. Nie manipulacja — usunięcie tarcia. Każda bariera, która normalnie sprawia, że zatrzymujesz się i zastanawiasz — szukanie dokumentacji, wymyślanie odpowiedniego wzorca, wypisywanie boilerplate’u — zniknęła. Dystans między “mógłbym to ulepszyć” a “ulepszyłem to” skurczył się do sekund. A bez tego tarcia, bez tych mikro-pauz, w których mógłbyś zerknąć na zegarek albo poczuć pieczenie oczu albo przypomnieć sobie, że masz spotkanie o 8 rano, sesja po prostu… trwa.
Higiena nie polega na zatrzymywaniu się. Polega na zauważaniu. Zauważaniu, że siedzisz przy tym od trzech godzin. Zauważaniu, że cel zmienił się z “naprawić jedną funkcję” na “zrefaktorować wszystko”. Zauważaniu, że satysfakcja, którą czujesz, jest prawdziwa, ale output jest pusty.
Quiz mierzy dwa wymiary, które bezpośrednio mapują się na ten wzorzec: Eskalacja sesji (spirala “jeszcze jedna rzecz”) i Dark Flow (bezczasowa absorpcja, która czuje się jak produktywność, ale nie produkuje niczego). Zajmie ci trzy minuty. Jest anonimowy. I w przeciwieństwie do mojego refaktoru o 3 w nocy, może ci faktycznie powiedzieć coś użytecznego.
OnTilt to projekt badawczy badający mechanizmy uzależnień behawioralnych w narzędziach AI do kodowania. Quiz jest narzędziem autorefleksji, nie instrumentem diagnostycznym. Więcej na stronie O projekcie.