Pubblicato il 13 Settembre 2026 · di Ismail Nasry
In breve: Rewrite totale, strangler pattern o refactoring incrementale? Una guida decisionale per modernizzare sistemi legacy valutando rischio, dipendenze, rollback e continuità operativa.
Un sistema legacy raramente diventa un problema perché è semplicemente “vecchio”. Diventa un problema quando rallenta le decisioni, rende ogni modifica imprevedibile, concentra il rischio in pochi punti e costringe il team a scegliere tra velocità e stabilità.
A quel punto la domanda arriva quasi sempre nella forma sbagliata: conviene riscrivere tutto o continuare a mettere patch?
La scelta utile non è tra coraggio e prudenza. È tra strategie di cambiamento con profili di rischio diversi. Un rewrite totale, uno strangler pattern e un refactoring incrementale possono essere tutti corretti. La decisione dipende da dipendenze, reversibilità, pressione operativa e capacità del team di controllare la transizione.
Il vero problema non è il codice vecchio
Due sistemi con la stessa età possono avere condizioni completamente diverse. Un’applicazione di dieci anni, ben osservabile, coperta da test critici e con confini abbastanza chiari può essere più facile da evolvere di un sistema recente cresciuto senza ownership, senza metriche e con dipendenze nascoste.
Prima di decidere come modernizzare conviene distinguere problemi diversi. Il debito può essere strutturale, quando l’architettura rende ogni modifica fragile; operativo, quando deploy e rollback sono incerti; di conoscenza, quando la logica importante vive nella testa di poche persone; di integrazione, quando dipendenze e scambi dati sono poco tracciati; oppure di prodotto, quando il sistema continua a incorporare funzioni che il business non considera più centrali.
Se tutto viene compresso nell’etichetta “legacy”, il rischio è scegliere una soluzione troppo grande o troppo piccola.
Le tre strategie principali
Rewrite totale
Il rewrite sostituisce il sistema esistente con una nuova implementazione costruita in parallelo o quasi. Ha senso quando il modello attuale è diventato un vincolo profondo: architettura, dominio, stack e modalità operative sono così intrecciati che continuare a intervenire localmente produce costi crescenti.
Il vantaggio principale è la libertà progettuale. Il nuovo sistema può nascere con confini migliori, uno stack più adatto, osservabilità moderna e una struttura coerente con il dominio attuale.
Il problema è che un rewrite sposta molto rischio nel futuro. Per mesi il nuovo sistema può sembrare più pulito senza aver ancora dimostrato di gestire tutte le eccezioni, le integrazioni e i comportamenti accumulati nel vecchio. Il rischio più sottovalutato non è scrivere troppo codice: è dimenticare conoscenza implicita.
Strangler pattern
Lo strangler sostituisce il legacy per parti, facendo convivere vecchio e nuovo durante la transizione. Una funzione, un flusso o un confine di dominio viene intercettato e spostato progressivamente sul nuovo sistema. Il vecchio si restringe mentre il nuovo cresce.
Questa strategia riduce il blast radius: se una parte nuova fallisce, l’intero sistema non deve essere necessariamente sostituito o bloccato. In cambio aumenta temporaneamente la complessità. Bisogna gestire routing, sincronizzazione dati, compatibilità, doppie logiche e confini transitori.
Lo strangler è potente quando il team sa governare questa fase ibrida; può diventare pericoloso se la transizione resta senza una direzione chiara.
Refactoring incrementale
Il refactoring incrementale mantiene il sistema esistente e ne migliora progressivamente struttura, testabilità, dipendenze e componenti critici. È spesso la scelta più pragmatica quando il sistema funziona, il business non può tollerare grandi interruzioni e i problemi sono localizzabili.
Il vantaggio è la reversibilità: molti interventi possono essere rilasciati in piccoli passi, osservati e corretti rapidamente. Il limite è che non tutti i sistemi sono realmente rifattorizzabili in modo efficace. Se ogni confine è attraversato da dipendenze globali o se il modello dati è incompatibile con la direzione futura, il refactoring rischia di diventare manutenzione costosa mascherata da modernizzazione.
Primo criterio: quanto rischio puoi isolare?
La domanda più utile non è “quanto è brutto il codice?”, ma “quanto bene possiamo isolare il rischio del cambiamento?”.
Un sistema facile da segmentare favorisce una strategia incrementale. Un sistema monolitico solo dal punto di vista del deploy, ma con confini logici leggibili, può essere strangolato pezzo per pezzo.
Al contrario, un sistema in cui ogni flusso dipende da stato globale, job nascosti, tabelle condivise e integrazioni non documentate rende più difficile qualsiasi sostituzione parziale. In quel caso il primo progetto di modernizzazione potrebbe non essere la riscrittura, ma la riduzione dell’incertezza: mappare dipendenze, aggiungere telemetria, identificare flussi critici e costruire punti di osservazione.
Secondo criterio: reversibilità
Una buona strategia di modernizzazione mantiene il maggior numero possibile di decisioni reversibili finché non emergono dati reali.
Un cambiamento reversibile permette di testare una direzione senza trasformarla subito in un vincolo permanente. Per esempio, estrarre un singolo servizio dietro un’interfaccia chiara è generalmente più reversibile che migrare in una sola volta l’intero modello dati. Allo stesso modo, introdurre un nuovo flusso per una percentuale limitata di traffico consente di osservare il comportamento prima del cutover completo.
Più una decisione è difficile da invertire, più dovrebbe essere supportata da evidenze operative e non solo da preferenze architetturali.
Terzo criterio: dipendenze e blast radius
Il blast radius misura quanta parte del sistema o del business può essere colpita quando qualcosa va male. Durante una modernizzazione, questo concetto dovrebbe guidare la sequenza dei lavori.
Una componente ad alto accoppiamento, centrale nei flussi economici o operativi, non è necessariamente il posto migliore da cui iniziare. Potrebbe avere alto valore, ma anche un costo di errore elevato.
Spesso conviene partire da un’area che abbia valore sufficiente da giustificare il lavoro, confini abbastanza chiari e una possibilità concreta di rollback o fallback. Questo crea un ambiente reale in cui verificare stack, processi, osservabilità e capacità del team prima di affrontare le parti più rischiose.
Quarto criterio: continuità operativa
Un sistema legacy non esiste in laboratorio. Gestisce ordini, utenti, contenuti, processi interni, documenti, pagamenti o integrazioni che devono continuare a funzionare durante il cambiamento.
Per questo la modernizzazione deve essere progettata anche come problema operativo. Prima di spostare una parte critica bisogna sapere come rilevare un errore, come distinguere un problema nuovo da uno già presente, quale fallback usare, quanto tempo richiede il rollback, se i dati possono essere riconciliati e chi ha l’autorità di fermare il rollout.
Se queste risposte mancano, il problema non è ancora quale strategia scegliere. Manca la capacità di controllare il cambiamento.
Quinto criterio: quanto conosci davvero il comportamento attuale?
Molti rewrite falliscono concettualmente prima ancora del codice: il nuovo team assume che il sistema corrente sia pienamente compreso.
In realtà una parte importante della specifica può vivere in comportamenti non documentati, workaround operativi, eccezioni gestite manualmente, query di supporto e dipendenze nate nel tempo.
Per questo, prima di sostituire un sistema, conviene trattare l’osservazione del legacy come una fase di discovery. Log, metriche, audit trail, tracing dove possibile e analisi dei flussi reali possono rivelare quali parti del sistema sono davvero centrali e quali esistono solo per inerzia.
Il legacy, in questo senso, non è soltanto qualcosa da eliminare. È una fonte di requisiti impliciti.
Quando il rewrite totale è la scelta sensata
Un rewrite diventa più difendibile quando diverse condizioni si presentano insieme: il modello architetturale attuale impedisce cambiamenti fondamentali, il dominio è cambiato al punto che la struttura esistente rappresenta male il business, le dipendenze possono essere replicate o sostituite con un piano verificabile e il team può sostenere per un periodo la convivenza tra vecchio e nuovo.
Serve inoltre una strategia esplicita di migrazione dati e cutover, e la nuova implementazione deve poter prioritizzare le funzioni invece di inseguire una parità totale fin dall’inizio.
Il rewrite è invece molto più fragile quando nasce soprattutto da frustrazione tecnica. “Il codice è brutto” non è un business case. “Non riusciamo più a modificare il flusso ordini senza settimane di regressioni, e il costo del cambiamento sta bloccando il prodotto” è già una formulazione più utile.
Quando preferire uno strangler
Lo strangler è spesso adatto quando il sistema è strategico ma può essere separato per capacità o domini. Funziona bene quando il business non può fermarsi per una migrazione unica, alcuni flussi sono molto più urgenti di altri, esistono confini che possono diventare API, servizi o moduli autonomi e il team può gestire temporaneamente un’architettura ibrida.
La condizione critica è avere un piano di uscita. Ogni componente introdotto durante la transizione dovrebbe chiarire se è definitivo o temporaneo. Altrimenti proxy, sincronizzazioni e adapter nati per sei mesi possono diventare il nuovo legacy.
Quando il refactoring incrementale è sufficiente
Non ogni sistema legacy deve essere sostituito. Se i problemi sono concentrati, il dominio è ancora valido e la piattaforma continua a sostenere i requisiti del business, un refactoring ben guidato può produrre più valore di un progetto di riscrittura.
I segnali favorevoli sono abbastanza concreti: è possibile aggiungere test sui flussi critici, i confini interni possono essere rafforzati senza riscrivere tutto, i colli di bottiglia sono noti e deploy e rollback possono essere migliorati gradualmente.
In questi casi il rischio maggiore è trasformare il refactoring in un progetto infinito. Servono obiettivi verificabili: ridurre i tempi di rilascio, isolare un dominio, eliminare una dipendenza critica, migliorare la capacità di rollback o diminuire il numero di componenti toccati per completare un singolo flusso.
Un esempio ipotetico
Immaginiamo un gestionale che centralizza clienti, ordini, fatturazione e notifiche in un’unica applicazione. Il team vuole sostituirlo perché ogni rilascio richiede test manuali estesi. Un rewrite completo sembra attraente.
Analizzando il sistema, però, emerge che ordini e fatturazione condividono fortemente dati e logica, mentre le notifiche hanno confini più semplici e causano frequenti problemi operativi.
Una strategia ragionevole potrebbe iniziare introducendo osservabilità sui flussi principali, poi isolare l’invio notifiche dietro un’interfaccia stabile e spostare quel flusso su un nuovo componente. A quel punto il team può verificare deploy, rollback e metriche nel nuovo modello e usare ciò che ha imparato per decidere se estrarre altri domini o affrontare una riscrittura più ampia.
In questo scenario non si sceglie lo strangler come dottrina. Si usa una sostituzione progressiva per ridurre l’incertezza prima di prendere decisioni irreversibili.
I segnali che dovrebbero fermare un progetto
Una modernizzazione ha bisogno anche di criteri di stop. Continuare perché “abbiamo già investito molto” è una delle forme più costose di inerzia.
Il progetto va rivalutato se il nuovo sistema accumula rapidamente le stesse dipendenze del vecchio, se la migrazione dati richiede eccezioni sempre più difficili da controllare, se la convivenza tra sistemi genera più incidenti del previsto o se non esiste più una sequenza credibile per spegnere il legacy.
Un altro segnale importante è l’assenza di miglioramenti operativi. Se il team sta ricreando parità funzionale senza rimuovere funzioni obsolete e senza migliorare rilasci, rollback, osservabilità o velocità di cambiamento, il progetto rischia di produrre solo una nuova base di codice.
Un criterio di stop non significa abbandonare la modernizzazione. Può significare cambiare strategia prima che il costo di inversione diventi troppo alto.
Una matrice decisionale semplice
Se il sistema è separabile, la continuità operativa è critica e il rischio deve essere distribuito, lo strangler è spesso un buon candidato.
Se il sistema è ancora valido ma ha problemi localizzati e migliorabili, il refactoring incrementale tende a essere più economico e reversibile.
Se architettura, dominio e vincoli fondamentali non sono più recuperabili, e il team può sostenere una transizione controllata, il rewrite diventa più credibile.
Se invece non sono chiare dipendenze, comportamento reale e capacità di rollback, nessuna delle tre strategie è ancora la priorità. Prima serve ridurre l’incertezza.
La decisione migliore conserva opzioni
La modernizzazione legacy viene spesso raccontata come un progetto di sostituzione tecnologica. In realtà è soprattutto un esercizio di gestione del rischio.
La strategia più forte non è necessariamente quella che promette l’architettura più pulita alla fine. È quella che mantiene il business operativo, limita il blast radius, rende osservabili gli effetti del cambiamento e conserva abbastanza opzioni per correggere la direzione.
Prima di riscrivere, conviene quindi chiedersi non soltanto “come vogliamo che sia il nuovo sistema?”, ma anche “quali decisioni possiamo permetterci di sbagliare senza fermare il business?”.
Quella risposta spesso indica la strategia giusta molto più chiaramente del diagramma architetturale.
Lavoriamo insieme
Hai bisogno di supporto su questo tema? Sviluppo soluzioni su misura per le tue esigenze.






