Un refactor non ha sintomi — non vedi quando Claude sbaglia. Tre guardrail: test, commit atomici, clic manuale.
Il refactoring è il compito in cui Claude è più pericoloso. Un bug ha sintomi — il bottone non fa niente, il valore è undefined, stack trace — quindi riesci a dire se il fix di Claude funziona. Un refactor non ne ha. "Ancora gira" può voler dire "i test ancora passano" mentre il comportamento è silenziosamente cambiato, non te ne sei accorto, e una settimana dopo la produzione esplode.
Di recente ho fatto un refactor abbastanza grosso con Claude su how2claude: migrazione dei pagamenti crypto x402 da un PaymentHandler + FacilitatorClient scritti a mano (139 righe) alla gem x402-rails, e nello stesso momento estrazione del mapping di campi di Purchase.create! / Subscription.create! — duplicato in due controller — in metodi di classe sul model. Un commit, 4 file modificati, 2 cancellati, 2 aggiunti.
Il mio prompt era una parola: "refactor."
Poteva essere così breve perché intorno c'erano i guardrail.
Quando il branch è arrivato a quel punto aveva 221 test. Tutti i percorsi critici del flusso di pagamento erano coperti.
L'azione di default di Claude prima di un refactor non è "guarda prima i test." Quindi gli ho detto di lanciare bin/rails test prima, confermare verde, e poi toccare qualunque cosa.
Dopo il refactor, lanciarli di nuovo. Ancora verdi. Questo non significa zero regressioni — significa che il comportamento noto non è rotto.
Se il tuo codepath non ha copertura di test, fai scrivere a Claude un test minimo che blocchi il comportamento attuale. Fallo girare, committalo. Poi refactor. Altrimenti quello che sta facendo non è refactor, è riscrittura — e non hai modo di verificare l'equivalenza.
Questo refactor erano in realtà due cose:
Purchase / Subscription: controller → metodi di classe sul modelSul frontend c'era una terza: riscrivere il flusso di signing lato JS con viem + x402-fetch.
Ho fatto dividere a Claude sui confini naturali: backend + estrazione del model in un commit (9f3e239), frontend in un commit separato (93746d8). Ogni commit porta una descrizione completa, elenco file, e il perché del cambio.
Benefici:
- I diff restano leggibili. Un commit, una cosa.
- Granularità di rollback. Se la produzione incappa in un bug frontend, git revert 93746d8 rollbacka solo il frontend, lasciando il backend.
- L'attenzione di Claude stessa resta focalizzata. Un commit, una cosa — e la sua attenzione copre solo quella cosa.
Una volta completato il refactor, faccio fermare Claude e gli faccio mostrare git diff --staged. Non lanciare i test. Non lanciare l'app. Leggi il diff prima.
Segnali che scansiono:
app/services/x402/payment_handler.rb cancellato per intero — ok, quello è il punto della migrazione alla gem. Ma se cancella qualcosa che non gli ho chiesto di toccare, mi fermo e chiedo.Purchase.create!(wallet_address: verify_result["payer"], ...) → Purchase.record_x402!(payment:, settlement:) ora legge payment[:payer]. La sorgente è cambiata (request.env della gem vs valore di ritorno del vecchio client), ma i campi devono corrispondere uno a uno.Trappola 1: i controller Stimulus della gem non si sono caricati in silenzio
La gem x402-rails porta con sé i propri Stimulus controller. Claude ha scritto il codice, i test sono passati verdi. Ho cliccato manualmente il bottone di pagamento — niente.
Motivo: config/importmap.rb aveva un pin per @hotwired/stimulus che puntava a un file vendor inesistente, e importmap ha scartato quel pin silenziosamente. I controller della gem non si sono mai caricati. I test non possono beccare questo perché bin/rails test non esegue JS.
Trappola 2: YAML ha parsato 0x... come intero
wallet_address: 0x833589... — senza virgolette. YAML ha visto il prefisso 0x, l'ha letto come intero esadecimale. Il facilitator ha ricevuto un non-stringa e ha rifiutato. Claude non si è fermato a pensare alle regole di parsing YAML mentre scriveva la config.
Entrambe le trappole sono state prese perché ho cliccato il bottone vero. Test passati non è la stessa cosa di feature funzionante. La verifica manuale dopo un refactor non è opzionale.
Il refactoring è lo scenario "lascia guidare Claude" a più alto rischio. I guardrail non sono per Claude. Sono per te — perché quando Claude sbaglia qualcosa, lo prendi in 5 minuti invece che in un incendio di produzione.