Migliorare il codice senza cambiarne il comportamento. La disciplina che mantiene sane le codebase.
Il refactoring consiste nel migliorare la struttura interna del codice senza modificarne il comportamento esterno.
Questa definizione ha due parti fondamentali:
Migliorare la struttura: Rendere il codice più facile da comprendere, modificare o estendere. Questo include nomi migliori, funzioni più piccole, organizzazione più chiara, meno duplicazione.
Senza modificare il comportamento: Il codice fa esattamente ciò che faceva prima. Gli input producono gli stessi output. Gli effetti collaterali sono identici. I test continuano a passare.
Il refactoring non è:
Se stai cambiando ciò che fa il codice, non stai facendo refactoring—stai facendo qualcos'altro. Il refactoring riguarda specificamente la struttura, non il comportamento.
Il refactoring è come pulire la cucina. Il cibo ha ancora lo stesso sapore. Ma cucinare diventa più facile, veloce e meno frustrante.
Martin Fowler ha documentato dozzine di refactoring specifici, ciascuno con:
Ecco alcuni che userai costantemente:
Extract Function: Prendi del codice e mettilo in una nuova funzione con un nome chiaro. Questo è probabilmente il refactoring più comune.
Rename: Cambia il nome di una variabile, funzione o classe per esprimere meglio il suo scopo. I buoni nomi sono sorprendentemente importanti.
Inline Function: L'opposto di extract—sostituisci una chiamata di funzione con il suo corpo. Usa quando una funzione non aggiunge chiarezza.
Extract Variable: Prendi un'espressione complessa e dalle un nome assegnandola a una variabile.
Move Function: Sposta una funzione in una classe dove ha più senso.
Replace Conditional with Polymorphism: Sostituisci complessi if/else o switch con dispatch orientato agli oggetti.
Questi non sono refactoring "avanzati". Sono strumenti quotidiani. Imparali finché non diventano automatici.
Il refactoring è sicuro solo quando hai i test. Senza test, ogni modifica potrebbe rompere qualcosa, e non lo saprai fino alla produzione.
Il processo di sicurezza:
I piccoli passi sono fondamentali. Non cercare di fare refactoring di cinque cose contemporaneamente. Estrai una funzione, esegui i test, committa. Rinomina una variabile, esegui i test, committa. In questo modo, se qualcosa si rompe, sai esattamente cosa l'ha causato.
Gli IDE moderni hanno refactoring automatizzati che sono più sicuri delle modifiche manuali. "Rename" nel tuo IDE aggiorna tutti i riferimenti. "Extract Function" crea la funzione e aggiorna il punto di chiamata. Usa questi strumenti.
La Regola del Boy Scout: Lascia il codice migliore di come l'hai trovato. Quando tocchi il codice per qualsiasi motivo, fai un piccolo miglioramento. Nel tempo, la codebase diventa più pulita invece che più sporca.
Nessun Test = Nessuna Sicurezza
Il refactoring senza test è solo cambiare codice e sperare. Non è refactoring—è scommettere.
Fai refactoring costantemente. Il refactoring non è una fase separata o uno sprint. È continuo.
Trigger specifici per fare refactoring:
Prima di aggiungere una funzionalità: Se la struttura attuale rende difficile aggiungere la funzionalità, fai prima refactoring. Rendi facile il cambiamento, poi fai il cambiamento facile.
Dopo aver fatto passare un test: Questo è il "refactor" in red-green-refactor. Pulisci prima di passare al test successivo.
Quando noti un code smell: Codice duplicato, metodi lunghi, nomi poco chiari, condizionali complessi—questi sono segnali che serve refactoring.
Durante la code review: Se vedi qualcosa che miglioreresti, fallo o annotalo per dopo.
Quando hai tempo: Tieni un "backlog di refactoring" di problemi noti. Quando hai tempo, prendi qualcosa dalla lista.
Non fare refactoring quando:
Il debito tecnico è il costo accumulato di prendere scorciatoie. Ogni volta che dici "lo pulirò dopo" e non lo fai, aggiungi debito. Ogni volta che fai copia-incolla invece di estrarre, aggiungi debito.
Il debito accumula interessi. Il codice disordinato richiede più tempo per essere modificato. I bug si nascondono nella complessità. I nuovi membri del team faticano a capire. Il costo del cambiamento aumenta.
Il refactoring è come ripaghi il debito. Il refactoring regolare mantiene il debito gestibile. Trascurare il refactoring lascia che il debito si accumuli fino a quando la codebase diventa ingestibile.
L'intuizione chiave: Il refactoring non è un lusso. È manutenzione essenziale. Non chiedi il permesso di ripagare il debito—lo fai semplicemente come parte del tuo lavoro.
XP integra il refactoring nel processo. Ogni funzionalità, ogni correzione di bug, ogni task include tempo per lasciare il codice migliore di come l'hai trovato. Non hai bisogno di uno "sprint di refactoring"—hai bisogno di un'abitudine al refactoring.
Mentre correggi un bug, noti codice duplicato nelle vicinanze. Correggi prima il bug (modifica del comportamento), poi estrai la duplicazione in una funzione (refactoring). Due commit: correzione del bug, poi refactoring.
Mentre aggiungi una funzionalità, decidi anche di fare refactoring del sistema di autenticazione, riorganizzare la struttura delle cartelle e aggiornare lo schema del database. Perdi traccia di cosa è cambiato, i test falliscono misteriosamente e passi ore a fare debug.