Nello sviluppo di software son spesso necessari compromessi, scegliendo ad esempio quali parti vadano eseguite fin dall’inizio in maniera rigorosa e quali possano essere momentaneamente risolte in modo veloce ed economico, ma sapendo di dovercisi prima o poi dedicare. La speranza è che delle tante lavorazioni risolte con impegno ridotto, solo poche richiedano effettivamente una successiva, costosissima rilavorazione, e nessuna porti a danni nell’attività che quel software descrive.
Nell’informatica, il costo di rilavorazione accluso ad una soluzione viene spesso chiamato technical debt, o code debt: a ciascuna scelta vengono associati un costo e un debito: il costo viene affrontato subito, mentre il debito viene rimandato alle future generazioni.
La gestione dei sistemi legacy è un classico esempio di debito molto maggiore del costo e per il quale non è possibile escludere il rischio di andare incontro ad un collassamento del sistema, con perdita di attività e necessario disaster recovery.
Questa realtà è stata affermata in modo ancora più forte dall’avvento dello sviluppo attraverso la pipeline CI/CD. L’aggiornamento costante e l’inserimento di nuovi argomenti in chiave sempre più moderna (si pensi alla sicurezza o alle ottimizzazioni AI/ML) rende spesso impietoso il confronto tra la gestione dei sistemi legacy e la gestione dei sistemi moderni.
Ovviamente è possibile inserire i sistemi legacy in un contesto attuale, valutando la strategicità del sottosistema specifico. L’operazione può essere fatta a più livelli, sempre lasciando parte del debito alle generazioni future. Bisogna tenere in massima considerazione soluzioni che benché vecchie siano perfettamente funzionanti, in quanto sono perfettamente mappate sul business e quindi una loro riscrittura integrale potrebbe portare a qualche mismatch non informatico ma organizzativo.
Un assessment sul legacy code
Dovendo ridurre il technical debt almeno in parte, il paradigma di riferimento è l’approccio a microservizi e API.
L’analisi del codice legacy e dell’infrastruttura che lo esegue è la prima fase di un processo di modernizzazione. Serve qui un assessment proveniente da auditor esterno: l’azienda è in genere troppo legata alle scelte passate per essere obiettiva sulle scelte future.
Il software va suddiviso in blocchi funzionali con le loro comunicazioni. Si identificano anche le componenti infrastrutturali che lo eseguono. Volendo si può anche documentare il processo complessivo che questo software implementa. La modernizzazione può essere totale o parziale (in vari step) su codice, infrastruttura e processo.
A questo punto è possibile identificare i rischi che si corrono nel non affrontare i tre grandi aspetti dei sistemi legacy oggi:
- infrastruttura obsoleta
- codebase troppo variabile
- security by design
L’infrastruttura dev’essere moderna
Le vecchie infrastrutture non costano finché funzionano, ma quando smettono di funzionare possono essere difficili (tempo) o impossibili ad essere sostituite, generando un blocco del funzionamento della soluzione.
La soluzione è la migrazione della vecchia infrastruttura e la sua progressiva confluenza verso la nuova infrastruttura. È certamente necessario migrare parte del codice su runtime che indirizzano una nuova infrastruttura.
Costi e tempi associati saranno limitati, come un TCO su 3-5 anni può mostrare, con riduzione dei costi di personale e probabili benefici complessivi.
Ridurre la variabilità della codebase
A seconda del tipo di codebase legacy, i problemi possono avere ampiezze molto grandi. Anche se lentamente, gli sviluppatori esperti in alcuni linguaggi tendono ad essere sempre di meno, sempre gli stessi (con relativo skill gap) e sempre più difficili da integrare in pipeline moderne. Il technical debt relativo diventa sempre più facile da affrontare.
La soluzione è sviluppare un’analisi del codice ed operare la relativa completa riscrittura. In alcuni casi ci si può affidare a strumenti automatici che generano direttamente il nuovo codice e la relativa documentazione.
Implementare la security by design
La complessità tecnica e normativa dell’attuale mondo connesso richiede forzatamente un codice scritto a partire dalla sicurezza, secondo il principio della security by design. I vecchi sistemi sono stati sviluppati in altra maniera e anche se spesso sono piuttosto solidi non seguono i requisiti moderni. L’attacker, ma curiosamente anche il legislatore, potrebbero mettere sotto stress un sottosistema non moderno.
La gestione della sicurezza passa oggi per una codebase e relativa infrastruttura gestita con criteri SecDevOps, senza nessuna eccezione.
La soluzione ideale resta sempre la riscrittura da zero del codice, con relativa analisi di processo e sua reingegnerizzazione prima della riscrittura. Certo, ci si può limitare ad una analisi del codice con riscrittura in microservizi dei servizi più stressati e l’incapsulamento delle parti più intricate con relativa fruizione tramite API. Quale sia la scelta finale, il codice dev’essere gestibile da pipeline moderne e l’infrastruttura dev’essere di facile manutenzione.