Free

Niech Claude refaktoruje

Refaktoring nie ma objawów — błędów Claude'a nie widać. Trzy barierki: testy, atomowe commity, ręczne klikanie.


Refaktoring to zadanie, w którym Claude jest najbardziej niebezpieczny. Bug ma objawy — przycisk nie reaguje, wartość undefined, stack trace — więc możesz powiedzieć, czy fix Claude'a zadziałał. Refaktoring nie ma. „Wciąż działa" może oznaczać „testy wciąż przechodzą", gdy tymczasem zachowanie cicho się zmieniło, nie zauważyłeś, a tydzień później produkcja płonie.

Zrobiłem niedawno z Claude'em dość duży refaktoring w how2claude: przeniosłem płatności krypto x402 z ręcznie napisanych PaymentHandler + FacilitatorClient (139 linii) na gem x402-rails, a jednocześnie wyciągnąłem mapowanie pól Purchase.create! / Subscription.create! — zduplikowane w dwóch kontrolerach — do metod klasowych modelu. Jeden commit, 4 pliki zmienione, 2 usunięte, 2 dodane.

Mój prompt miał jedno słowo: „refactor."

Mógł być tak krótki, bo wokół stały barierki.

Barierka #1: nigdy refaktoringu bez testów

Gdy branch doszedł do tego punktu, miał 221 testów. Wszystkie krytyczne ścieżki flow płatności były pokryte.

Domyślne działanie Claude'a przed refaktoringiem to nie „najpierw zajrzyj w testy." Więc kazałem mu uruchomić bin/rails test najpierw, potwierdzić zielone, i dopiero wtedy czegokolwiek dotykać.

Po refaktoringu puścić jeszcze raz. Wciąż zielone. To nie znaczy „brak regresji" — znaczy, że znane zachowanie nie jest złamane.

Jeśli twój codepath nie ma pokrycia testowego, każ Claude'owi napisać minimalny test, który utrwala obecne zachowanie. Puść go, zacommituj. Dopiero wtedy refaktoring. Inaczej to, co on robi, to nie refaktoring, tylko przepisywanie — i nie masz sposobu zweryfikować ekwiwalencji.

Barierka #2: każ mu podzielić zmianę na atomowe commity

Ten refaktoring w rzeczywistości był dwoma rzeczami:

  1. Backend x402: ręcznie pisany → gem
  2. Mapowanie pól Purchase / Subscription: kontroler → metody klasowe modelu

Na froncie była trzecia: przepisać flow podpisu po stronie JS z viem + x402-fetch.

Kazałem Claude'owi dzielić po naturalnych granicach: backend + ekstrakcja modelu w jednym commicie (9f3e239), frontend w osobnym (93746d8). Każdy commit nosi pełny opis, listę plików i uzasadnienie zmiany.

Korzyści:
- Diffy pozostają czytelne. Jeden commit, jedna rzecz.
- Granularność rollbacku. Jeśli produkcja złapie buga frontendu, git revert 93746d8 cofa tylko front, zostawiając backend.
- Uwaga samego Claude'a pozostaje skupiona. Jeden commit, jedna rzecz — i jego uwaga pokrywa tylko tę jedną.

Barierka #3: przeczytaj diff zanim powiesz „gotowe"

Gdy refaktoring jest skończony, zatrzymuję Claude'a i każę pokazać git diff --staged. Nie uruchamiaj testów. Nie odpalaj aplikacji. Najpierw czytaj diff.

Sygnały, które skanuję:

  • Co usunął? app/services/x402/payment_handler.rb wywalony w całości — ok, to sens migracji na gem. Ale jeśli usunął coś, czego nie prosiłem dotykać, zatrzymuję się i pytam.
  • Czy mapowanie pól się zmieniło? Purchase.create!(wallet_address: verify_result["payer"], ...)Purchase.record_x402!(payment:, settlement:) teraz czyta payment[:payer]. Źródło się zmieniło (request.env z gema vs wartość zwracana przez stary klient), ale pola muszą mapować się jeden do jednego.
  • Zmiany „przy okazji." Claude uwielbia naprawiać rzeczy, które „wyglądają źle", podczas refaktoringu — przeformułowywać komunikaty błędów, zmieniać nazwy zmiennych, ekstraktować metodę, o której myśli, że powinna istnieć. Uważaj na to. Obietnica refaktoringu to „zachowanie ekwiwalentne." Zmiana przy okazji to łamie.

Dwie rzeczy, które Claude zrobił źle tym razem

Pułapka 1: Stimulus controllers gema cicho się nie załadowały

Gem x402-rails przychodzi z własnymi Stimulus controllers. Claude napisał kod, testy przeszły zielone. Kliknąłem ręcznie przycisk płatności — nic.

Powód: w config/importmap.rb pin dla @hotwired/stimulus wskazywał na nieistniejący plik vendor, a importmap cicho wyrzucił ten pin. Controllers gema nigdy się nie załadowały. Testy tego nie łapią, bo bin/rails test nie wykonuje JS.

Pułapka 2: YAML sparsował 0x... jako integer

wallet_address: 0x833589... — bez cudzysłowów. YAML zobaczył prefiks 0x, odczytał jako szesnastkową liczbę całkowitą. Facilitator dostał non-stringa i odrzucił. Claude nie zatrzymał się, żeby pomyśleć o regułach parsowania YAML, gdy pisał config.

Obie pułapki zostały złapane, bo kliknąłem prawdziwy przycisk. Zielone testy to nie to samo, co działająca funkcjonalność. Ręczna weryfikacja po refaktoringu nie jest opcjonalna.

Pełny flow refaktoringu z Claude'em

  1. Uruchom całą suite testów. Potwierdź zielone. Jeśli docelowy kod nie ma pokrycia, każ Claude'owi napisać minimalny test, który utrwala obecne zachowanie, i zacommituj.
  2. Powiedz, jaką powierzchnię chcesz wyciągnąć. „Refactor" wystarczy, gdy duplikacja jest oczywista; gdy nie, powiedz „wyciągnij mapowanie pól z X do metod klasowych w Y."
  3. Podziel na atomowe commity. Jeden commit, jedna rzecz.
  4. Czytaj diff sam. Co zostało usunięte, czy mapowanie pól się zgadza, czy nie zmieniło czegoś przy okazji.
  5. Puść testy jeszcze raz. Zielone.
  6. Jeśli dotyka ścieżki widocznej dla użytkownika, kliknij przez funkcjonalność ręcznie. Warstwy, do których testy nie docierają — JS, importmap, CDN, parsowanie YAML — musisz zobaczyć własnymi oczami.

Refaktoring to scenariusz „puść Claude'a za kierownicę" o najwyższym ryzyku. Barierki nie są dla Claude'a. Są dla ciebie — żebyś, kiedy Claude coś spierniczy, złapał to w 5 minut, a nie w pożarze na produkcji.