Prima o poi capita a tutti.
Apri una parte del codice che devi modificare e pensi:
“Qui sarebbe meglio rifare tutto.”
Il codice è difficile da leggere, ci sono condizioni ovunque e le responsabilità sono mescolate. Alcune decisioni sembrano non avere più senso. E magari ti chiedi come sia stato possibile scriverlo in quel modo. La tentazione è forte:
cancellare tutto e ricominciare da zero.
A volte è esattamente la scelta giusta, molto più spesso, però, non lo è, perché tra correggere un codice esistente e riscriverlo completamente non esiste una risposta universale.
La vera domanda è:
Quale delle due opzioni ci permette di ottenere un risultato migliore con un rischio accettabile?
Il codice vecchio non è necessariamente codice sbagliato
Quando trovi codice scritto anni fa, è facile giudicarlo con gli occhi di oggi.
Oggi conosci tecniche migliori, hai più esperienza. Le tecnologie sono cambiate e magari esistono strumenti che all’epoca non c’erano che possono aiutarti, ma quel codice potrebbe essere stato una soluzione perfettamente ragionevole nel momento in cui è stato scritto. Questo non significa che debba rimanere così per sempre, ma solo che il fatto che un codice sia vecchio non è una ragione sufficiente per riscriverlo.
Prima bisogna capire quale problema abbiamo realmente davanti.
Funziona ancora?
La prima domanda è sorprendentemente semplice:
Quel codice funziona?
Se una parte dell’applicazione è stabile, non presenta problemi e viene modificata raramente, riscriverla soltanto perché non ci piace come è stata implementata potrebbe essere una pessima idea.
Ogni riscrittura introduce infatti un rischio.
Puoi introdurre nuovi bug.
Puoi dimenticare comportamenti particolari.
Puoi cambiare involontariamente qualcosa che gli utenti si aspettano.
Puoi scoprire casi limite che il vecchio codice gestiva senza che nessuno se ne accorgesse.
Il codice esistente ha almeno un vantaggio:
sai che, nel mondo reale, è già stato utilizzato.
Il codice che non tocchi non ha sempre bisogno di essere migliorato
Questo è un principio importante.
Non tutto il codice deve essere perfetto, se una parte del sistema funziona e nessuno deve modificarla, il valore di una riscrittura potrebbe essere molto basso. Potresti investire giorni per ottenere un codice più elegante che non produce nessun beneficio concreto.
Il software non viene premiato perché è bello da guardare.
Il software viene premiato perché risolve problemi.
Se una parte del codice è brutta ma stabile, isolata e raramente modificata, forse non è lì che devi spendere il tuo tempo.
Quando correggere è meglio che riscrivere
Nella maggior parte dei casi, la prima opzione da considerare dovrebbe essere una modifica graduale.
Hai una funzione troppo grande? Puoi estrarre una responsabilità.
Hai una condizione difficile da capire? Puoi semplificarla.
Hai del codice duplicato? Puoi valutare se la duplicazione sia realmente un problema.
Hai una dipendenza difficile da sostituire? Puoi isolare il punto in cui viene utilizzata.
Non devi necessariamente buttare via tutto per migliorare una parte del sistema.
Spesso puoi ridurre progressivamente il problema.
Una modifica alla volta.
Ma attenzione al codice che si continua a rompere
C’è però un altro segnale.
Se ogni volta che tocchi quella parte del sistema rompi qualcos’altro, il problema non è più soltanto estetico. Hai una parte del codice che sta diventando un ostacolo allo sviluppo. Immagina una funzionalità che dovrebbe richiedere una giornata di lavoro. Ogni volta che viene modificata, però, servono tre giorni perché bisogna capire una quantità enorme di dipendenze e correggere problemi collaterali.
Se questo accade una volta, può essere sfortuna, ma se succede continuamente, probabilmente hai un problema strutturale.
In quel caso la domanda cambia:
“Quanto ci costa continuare a mantenere questa soluzione?”
Il costo della riscrittura
Riscrivere sembra spesso più semplice di quanto sia realmente.
Quando guardi il codice esistente vedi tutte le sue imperfezioni, quando invece immagini una riscrittura vedi soltanto la nuova versione. Non vedi ancora tutti i problemi che dovrai affrontare.
Una riscrittura significa anche dover recuperare:
- Requisiti impliciti
- Casi limite
- Comportamenti non documentati
- Integrazioni
- Dipendenze
- Dati esistenti
- Aspettative degli utenti
E soprattutto significa perdere una parte della conoscenza incorporata nel vecchio codice.
Il codice può essere brutto, ma potrebbe contenere anni di correzioni e adattamenti a problemi reali.
Riscriverlo significa anche riscrivere quella conoscenza.
Il problema della “riscrittura perfetta”
Un altro rischio è pensare:
“Questa volta lo facciamo bene.”
È una frase pericolosa.
Perché “bene” può trasformarsi rapidamente in:
- Nuova architettura
- Nuove librerie
- Nuove convenzioni
- Nuovi pattern
- Nuova infrastruttura
- Nuove modalità di gestione dello stato
Alla fine non stai più risolvendo il problema originale, ma costruendo un sistema completamente nuovo. E più grande diventa la riscrittura, più difficile diventa controllare il rischio. Una riscrittura non dovrebbe essere un’occasione per inserire tutto ciò che hai sempre voluto provare. Dovrebbe risolvere un problema concreto.
Quando riscrivere può essere la scelta giusta
Nonostante i rischi, ci sono situazioni in cui correggere continuamente il codice esistente non ha più senso.
Per esempio quando:
- L’architettura impedisce di implementare facilmente nuove funzionalità
- Il codice è diventato quasi impossibile da comprendere
- Ogni modifica genera continuamente regressioni
- Una tecnologia fondamentale non è più supportata
- I vincoli iniziali non esistono più
- Il costo di mantenimento supera quello di una nuova implementazione
- La struttura attuale non permette più di soddisfare i requisiti del prodotto
In questi casi continuare ad aggiungere pezze può diventare più rischioso che ripartire, ma anche allora bisogna fare attenzione.
“Riscrivere” non significa necessariamente buttare via tutto.
Puoi riscrivere una parte, o sostituire un modulo.
Puoi migrare gradualmente una funzionalità.
Puoi far convivere temporaneamente vecchia e nuova implementazione.
La riscrittura non deve essere per forza un interruttore acceso/spento.
Il compromesso migliore spesso è la riscrittura incrementale
Una delle strategie più efficaci consiste nel non scegliere tra:
“lascio tutto così”
e
“rifaccio tutto da zero”.
Puoi invece procedere per passi.
Prendi una parte del sistema, la isoli. La comprendi, la testi e la migliori.
Poi passi alla parte successiva.
In questo modo la nuova implementazione viene introdotta progressivamente e il rischio rimane più controllabile. Non è sempre possibile, ma quando lo è, spesso rappresenta un compromesso molto più sano di una grande riscrittura.
Prima di riscrivere, chiediti perché
Questa potrebbe essere la domanda più importante dell’intero articolo.
Prima di dire:
“Dobbiamo rifare tutto.”
chiediti:
“Quale problema vogliamo risolvere?”
Se la risposta è:
“Il codice non mi piace.”
non è ancora abbastanza, ma se invece la risposta è:
“Ogni modifica richiede troppo tempo.”
oppure:
“Non riusciamo più a implementare questa funzionalità senza introdurre regressioni.”
allora hai già un problema concreto da affrontare.
La riscrittura deve essere una soluzione a un problema, non una reazione emotiva davanti a del codice brutto.
Anche correggere può diventare un problema
Naturalmente vale anche il contrario.
A volte continuiamo a correggere perché abbiamo paura di riscrivere.
Aggiungiamo una condizione.
Poi un’altra.
Poi una nuova eccezione.
Poi un workaround.
Ogni modifica risolve il problema immediato, ma rende il sistema un pò più difficile da comprendere. Fino a quando arriva un punto in cui stiamo spendendo più tempo per mantenere le correzioni che ne avremmo speso per ripensare quella parte. È il momento in cui bisogna avere il coraggio di riconoscere che la soluzione attuale ha raggiunto il suo limite.
Non esiste una risposta definitiva
Non c’è una regola secondo cui bisogna sempre correggere e non ce n’è nemmeno una secondo cui il codice vecchio dovrebbe essere riscritto.
La decisione dipende da una combinazione di fattori:
quanto costa modificarlo, quanto spesso viene modificato, quanto è rischioso cambiarlo e quanto valore possiamo ottenere da una nuova soluzione.
Queste sono le domande che dovrebbero guidare la scelta.
Non quanto ci piace il codice, non quale tecnologia ci piacerebbe utilizzare, non quanto sarebbe bello avere finalmente una versione “pulita”.
Il vero segno di maturità
La parte più difficile forse è accettare che non tutto il codice merita di essere migliorato. A volte la decisione più professionale è lasciare una parte del sistema esattamente com’è. Altre volte la decisione più professionale è ammettere che continuare a correggerla non ha più senso. La maturità non sta nel preferire la riscrittura alla correzione. Sta nel riuscire a valutare il costo di entrambe senza farsi guidare dall’ego.
Perché riscrivere codice può essere molto gratificante.
Ti permette di partire da zero.
Di eliminare gli errori del passato.
Di utilizzare tecnologie nuove.
Di costruire qualcosa esattamente come vuoi tu.
Ma prima di cancellare tutto, vale la pena chiedersi:
“Sto risolvendo un problema reale o sto semplicemente cercando di liberarmi di un codice che non mi piace?”
A volte la risposta sarà: correggilo.
A volte sarà: riscrivilo.
La vera competenza sta nel sapere riconoscere la differenza.