Legacy nello sviluppo di app — cosa sono, rischi e strategie

Autore: IT Sectr Pubblicato: 2026-07-27 Tempo di lettura: 7 min

Legacy — non è semplicemente codice vecchio. È un sistema funzionante che porta soldi all’azienda ma rallenta lo sviluppo. Nello sviluppo mobile, il legacy può essere scritto in Objective-C, usare librerie obsolete o pattern architetturali superati. Secondo un rapporto di CAST Software (2024), l’età media di una riga di codice nei progetti enterprise supera i 14 anni. La strategia di lavoro con il legacy determina se diventerà un freno o rimarrà un asset gestibile.

Punti chiave

  • Legacy — codice che funziona in produzione ma usa tecnologie o approcci obsoleti
  • Manutenere il legacy richiede comprensione delle decisioni storiche e refactoring accurato
  • Strategia di migrazione — sostituzione graduale dei moduli senza fermare il prodotto tramite Strangler Fig
  • Testare il legacy — i characterization test catturano il comportamento attuale prima del refactoring
  • L’età del codice di per sé non è il problema — il problema è la mancanza di test e visione architetturale

Cosa sono i legacy nello sviluppo di app

Legacy — codice o sistema che continua a funzionare in produzione ma non soddisfa più gli standard di qualità moderni. Il legacy può essere scritto in un linguaggio obsoleto (ad esempio, Objective-C invece di Swift), usare librerie non supportate o pattern architetturali considerati da tempo anti-pattern.

La caratteristica principale del legacy è l’assenza di test. Secondo la definizione di Michael Feathers (2004), il codice legacy è codice senza test. Se non è possibile modificare il comportamento in modo sicuro, il sistema è in stato legacy indipendentemente dall’età. Codice nuovo senza test unitari è legacy dal primo giorno.

Il legacy non è necessariamente negativo. Un sistema ben progettato in Java 8 può essere più affidabile e comprensibile di codice caotico in Kotlin con coroutine. L’età del codice non è un indicatore di qualità — ciò che conta è quanto facilmente il sistema può essere modificato ed esteso.

Perché il codice legacy è normale

Ogni sistema di successo diventa legacy col tempo. È un processo naturale: le tecnologie si evolvono più velocemente di quanto il codice possa essere riscritto. Un’app scritta 5 anni fa in Swift 2 è legacy oggi, anche se era moderna al momento della creazione.

Il valore commerciale del legacy è spesso sottovalutato. Il sistema funziona in modo affidabile, elabora transazioni, memorizza dati — riscriverlo comporta rischi. Secondo il Standish Group (2024), il 35% dei progetti di riscrittura completa termina con un fallimento. Economicamente non è giustificato liberarsi del legacy, ma imparare a lavorarci.

Le migliori strategie sono la migrazione graduale, incapsulare il vecchio codice dietro nuove interfacce e test automatizzati. Il legacy diventa un problema solo quando smette di essere modificabile a un costo prevedibile.

Principali segnali di un sistema legacy

Mancanza di test automatizzati — l’indicatore principale. Se dopo aver modificato una singola riga uno sviluppatore non può eseguire test e confermare che nulla si è rotto — hai a che fare con legacy. Un segnale aggiuntivo: il processo di deploy richiede ore e passaggi manuali.

La documentazione non corrisponde al codice — un altro indicatore. I diagrammi architetturali sono obsoleti, i commenti descrivono un comportamento che è già cambiato. Il tempo di apprendimento per un nuovo sviluppatore supera un mese — segno di elevata complessità e bassa manutenibilità.

Segnali aggiuntivi: architettura monolitica senza confini chiari, test manuali come metodo principale di verifica, pipeline CI lunga (più di 30 minuti), utilizzo di librerie senza versioni aggiornate e incapacità di aggiornare le dipendenze senza rompere i moduli correlati.

Il fenomeno del codice fragile — una modifica in un punto ne rompe tre altri. È conseguenza dell’alto accoppiamento, quando i moduli sanno troppo l’uno dell’altro. Più alto è l’accoppiamento, più velocemente il sistema passa nella categoria legacy.

Rischi del lavoro con codice obsoleto

Riduzione della velocità — il rischio principale. Aggiungere una funzionalità semplice richiede ore di studio del codice e giorni di test. Secondo Stripe (2024), gli sviluppatori spendono il 33% del loro tempo per superare il debito tecnico, direttamente correlato alla presenza di moduli legacy nel progetto.

Perdita di competenze — gli autori del codice originale lasciano l’azienda e la documentazione è incompleta. I nuovi sviluppatori temono di toccare moduli sconosciuti, portando all’effetto “codice congelato”: il modulo non si evolve ma continua a funzionare. Il bus factor di tali sistemi è criticamente basso.

Sicurezza — le librerie obsolete contengono vulnerabilità note. Usare OpenSSL 1.0.2 o versioni vecchie di Jackson in progetti Java è una via diretta a incidenti di sicurezza che possono costare reputazione e clienti all’azienda.

Demotivazione del team — lavorare con legacy senza una strategia di miglioramento riduce la soddisfazione degli sviluppatori. Il team smette di essere orgoglioso del prodotto, il turnover aumenta, rallentando ulteriormente lo sviluppo del sistema.

Strategie di refactoring del legacy

Characterization test — il primo passo prima di qualsiasi modifica al codice legacy. Esegui il codice con dati di input noti e registra l’output atteso. Questi test catturano il comportamento attuale come specifica. Golden master testing è una variante in cui l’output viene confrontato con un file di riferimento.

Analisi dei seam — trovare punti in cui l’accoppiamento può essere rotto senza modificare il comportamento. Michael Feathers identifica diversi tipi di seam: preprocessor seam, object seam, link seam. Object seam è il più comune: sostituire un oggetto reale con uno stub di test attraverso un’interfaccia.

Sprout method e Sprout class — tecniche per aggiungere nuovo codice accanto al vecchio, non al suo interno. Invece di modificare un metodo esistente, crea un nuovo metodo con la logica desiderata e chiamalo da quello vecchio. Ciò minimizza il rischio di rompere il codice funzionante.

Esempio: aggiungere logging al legacy

groovy
class LegacyPaymentProcessor {
    def process(payment) {
        // 200 righe di codice legacy che non dovrebbero essere toccate
        logPayment(payment) // metodo sprout
    }
    def logPayment(payment) {
        // nuovo codice aggiunto accanto al legacy
    }
}

Migrazione a uno stack tecnologico moderno

Pattern Strangler Fig — l’approccio raccomandato per la migrazione del legacy. Un nuovo modulo viene creato in parallelo, il traffico viene gradualmente reindirizzato dal vecchio al nuovo. Il vecchio modulo muore naturalmente quando smette di ricevere richieste. Il pattern minimizza i rischi e consente il rollback in caso di problemi.

Branch by Abstraction — tecnica in cui viene creata un’astrazione sopra l’implementazione vecchia e quella nuova. Il codice client passa all’astrazione e l’implementazione vecchia viene gradualmente sostituita. Esempio: sostituire il livello di rete da AFNetworking ad Alamofire tramite un protocollo unificato NetworkService.

Migrazione graduale — suddividere la transizione in piccoli passi: incapsulare il vecchio modulo → scrivere test → creare un nuovo modulo → eseguire in parallelo → rimuovere il vecchio modulo. Ogni passo termina con uno stato stabile del sistema, consentendo il deploy in qualsiasi momento.

Domande frequenti

Il legacy va completamente riscritto?

La riscrittura completa è l’opzione più rischiosa. Solo il 25% dei progetti di Big Rewrite ha successo nei tempi previsti. È meglio applicare il pattern Strangler Fig: sostituire i moduli gradualmente senza fermare il prodotto. Ogni iterazione porta valore commerciale e i rischi sono distribuiti nel tempo.

Come iniziare il refactoring del legacy senza test?

Inizia con characterization test: esegui il modulo con dati noti, registra il risultato. Golden master testing è un modo semplice per catturare il comportamento. Aggiungi test ogni volta che tocchi una riga di codice. Tra 6 mesi avrai una struttura che protegge dalle regressioni.

Quando è meglio non toccare il legacy?

Se il sistema è stabile, non richiede modifiche frequenti e non influisce sulla velocità di sviluppo di altri moduli — lascialo stare. Se funziona, non toccarlo è un approccio ragionevole per moduli legacy isolati con bassa frequenza di modifiche. Tocca il codice solo quando sono necessarie modifiche commerciali.

Come aggiornare le dipendenze in un progetto legacy?

Usa il versionamento semantico e aggiorna per fasi: patch → minor → major. Scrivi test di compatibilità per ogni libreria. Dependabot o Renovate automatizzano la creazione di PR di aggiornamento. Se una libreria è obsoleta, pianifica la sua sostituzione tramite un’astrazione.

In cosa si differenzia il legacy dal debito tecnico?

Il debito tecnico è una metafora per stimare il costo dei miglioramenti rinviati. Il legacy è un sistema o codice specifico che è già diventato obsoleto. Il debito tecnico può accumularsi in un mese, il legacy richiede tempo. Non tutto il debito tecnico diventa legacy, ma ogni legacy contiene debito tecnico.

Riepilogo

  • Legacy — codice senza test, indipendentemente dall’età. Codice nuovo senza copertura è legacy dal primo giorno
  • Età del codice — non è un problema. Il problema è l’alto accoppiamento, mancanza di test e documentazione
  • Characterization test — il primo passo prima di qualsiasi modifica a un modulo legacy per catturare il comportamento
  • Pattern Strangler Fig — una strategia di migrazione sicura con sostituzione graduale dei moduli
  • Sprout method — tecnica per aggiungere nuovo codice accanto al vecchio senza rischio di rottura
  • Il 35% delle riscritture complete fallisce — la migrazione graduale è più affidabile di Big Rewrite
  • Legacy isolato con bassa frequenza di modifiche è meglio non toccarlo

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche