Sistemi legacy
Modernizzare un sistema legacy .NET o Java senza fermare l'azienda
Perché la riscrittura totale è quasi sempre un rischio e come procedere per passi, mettendo in sicurezza ciò che oggi funziona.
Quasi ogni organizzazione ha un'applicazione che "regge tutto": scritta dieci o quindici anni fa in .NET Framework o Java, modificata da molte mani, poco documentata e temuta da chiunque debba metterci mano. Funziona, ed è proprio per questo che è difficile da cambiare. In molti anni di lavoro su piattaforme bancarie, assicurative e della pubblica amministrazione ho imparato che modernizzare non significa buttare via, ma far evolvere senza interrompere il servizio.
Perché il software legacy resiste
Un sistema legacy non è "vecchio software": è software che contiene anni di regole di business, eccezioni, correzioni e conoscenza che spesso non è scritta da nessun'altra parte. Ecco perché sostituirlo è più difficile di quanto sembri.
I segnali che è il momento di intervenire sono abbastanza riconoscibili:
- runtime, framework o librerie fuori supporto o non più aggiornati, con vulnerabilità note;
- rilasci manuali, lenti e rischiosi, che si fanno il meno possibile;
- assenza di test automatici: ogni modifica può rompere qualcosa altrove;
- difficoltà a trovare persone disposte e capaci di lavorarci;
- integrazioni sempre più complesse con servizi moderni, API e cloud.
Il problema della riscrittura totale
La tentazione è ripartire da zero con tecnologie nuove. In alcuni casi è la scelta giusta, ma il più delle volte il "big bang" porta con sé tre rischi:
- regole nascoste: il nuovo sistema deve replicare comportamenti che nessuno ricorda finché non mancano;
- doppio costo: per mesi bisogna mantenere il vecchio sistema e costruire il nuovo;
- bersaglio mobile: mentre si riscrive, il business continua a chiedere modifiche, che vanno fatte due volte.
Per questo preferisco un approccio incrementale, in cui ogni passo porta valore e può essere verificato.
Un percorso in sei fasi
1. Analisi dell'esistente
Prima di toccare il codice serve una mappa: moduli, dipendenze, versioni di framework e librerie, database, job schedulati, integrazioni in ingresso e in uscita. Soprattutto, serve capire dove si concentrano i problemi: quali parti cambiano spesso, quali generano più incidenti, quali sono critiche per il business.
2. Una rete di sicurezza
Il primo investimento non è in nuove funzionalità ma nella possibilità di cambiare senza paura:
- codice sotto controllo di versione e build ripetibile, idealmente in una pipeline CI;
- test di caratterizzazione, che fotografano il comportamento attuale – anche quando è discutibile – per accorgersi subito se una modifica lo altera;
- log e monitoraggio sufficienti a capire cosa succede in produzione.
3. Stabilizzazione
Aggiornamenti di sicurezza, rimozione di dipendenze abbandonate, correzione dei difetti ricorrenti e documentazione delle parti critiche. È la fase meno spettacolare, ma spesso è quella che riduce di più gli incidenti e i costi di manutenzione.
4. Strangler pattern: sostituire per parti
Lo strangler fig pattern consiste nel mettere davanti al sistema esistente una facciata – tipicamente un API gateway o un reverse proxy – e spostare progressivamente le funzionalità verso componenti nuovi. Gli utenti e i sistemi collegati non si accorgono del cambiamento: le richieste vengono instradate al vecchio o al nuovo codice a seconda di cosa è già stato migrato. Le nuove funzionalità nascono direttamente nei componenti moderni.
5. Aggiornamento delle piattaforme
Alcune indicazioni concrete per i due ecosistemi più diffusi in ambito enterprise:
| Ambito | Percorso tipico |
|---|---|
| .NET Framework → .NET moderno | Separare prima le librerie di logica (portabili tramite .NET Standard o multi-targeting), poi i progetti applicativi. Lo strumento .NET Upgrade Assistant aiuta nelle parti meccaniche. |
| Servizi WCF | CoreWCF permette di mantenere i contratti esistenti su .NET moderno; per i nuovi servizi conviene valutare REST o gRPC. |
| ASP.NET WebForms | Non esiste una migrazione diretta: l'interfaccia va riscritta, meglio per sezioni dietro la stessa facciata. |
| Java | Passaggi tra versioni LTS (ad esempio da Java 8 a 17 o 21), con attenzione alle librerie rimosse dal JDK e al passaggio dei namespace da javax.* a jakarta.* richiesto da Jakarta EE 9+ e Spring Boot 3. |
6. Rilascio continuo
Containerizzazione con Docker dove ha senso, pipeline CI/CD con GitHub Actions o Azure DevOps, ambienti di test allineati alla produzione. Rilasciare spesso, in piccoli lotti, è il modo più sicuro per continuare a evolvere il sistema.
Il database: il vero cuore
In molti sistemi legacy la logica più importante vive nel database: stored procedure, trigger, viste, job. SQL Server, Oracle e DB2 reggono carichi enormi da decenni e spesso non c'è motivo di cambiarli. Il consiglio è non migrare applicazione e database nello stesso momento: prima si modernizza uno strato, si stabilizza, poi eventualmente si interviene sull'altro.
E l'Intelligenza Artificiale?
I modelli linguistici sono un aiuto concreto nella modernizzazione: leggono codice datato e lo spiegano, generano bozze di documentazione e di test, suggeriscono traduzioni tra API vecchie e nuove. Restano però strumenti che richiedono revisione esperta: la responsabilità del comportamento del sistema resta di chi lo mantiene.
In sintesi
Modernizzare un sistema legacy è un percorso, non un progetto unico: rete di sicurezza, stabilizzazione e sostituzione per parti, con il servizio sempre attivo.
Se parte del percorso prevede anche di automatizzare attività oggi manuali attorno al sistema, può esserti utile l'articolo su come automatizzare i processi aziendali.