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:

AmbitoPercorso tipico
.NET Framework → .NET modernoSeparare 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 WCFCoreWCF permette di mantenere i contratti esistenti su .NET moderno; per i nuovi servizi conviene valutare REST o gRPC.
ASP.NET WebFormsNon esiste una migrazione diretta: l'interfaccia va riscritta, meglio per sezioni dietro la stessa facciata.
JavaPassaggi 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.

Marcello Bruno è Senior Software Architect e fondatore di BWareLab. Da oltre 20 anni progetta e fa evolvere piattaforme enterprise in .NET e Java per finanza, assicurazioni, pubblica amministrazione e industria. Profilo completo