Debito TecnicoSoftware DeliveryArchitettura

Debito tecnico: cos'è e cosa non è

Il debito tecnico non è codice vecchio né tecnologia obsoleta. È un vincolo strutturale che rallenta la tua capacità di modificare il sistema. Una guida per riconoscerlo, misurarlo e distinguerlo da ciò che non lo è.

QMates· Software Advisory9 aprile 20268 min

Il test operativo

La domanda sbagliata sul debito tecnico è "da quanto tempo questo codice è qui?" La domanda giusta è diversa: "rallenta la tua capacità di modificare il sistema nel contesto in cui ti trovi adesso?" Se la risposta è no, non è debito tecnico nel senso operativo del termine. Se la risposta è sì, lo è: indipendentemente da quando è stato scritto, da chi o con quale intenzione.

Questo test funzionale cambia il frame in modo sostanziale. Il debito tecnico non è una categoria morale sul codice passato; è una misura di resistenza al cambiamento presente. Un sistema scritto dieci anni fa, con tecnologie che nessuno usa più, può non essere debito se il team lo conosce bene e può modificarlo senza attriti. Un sistema scritto sei mesi fa, da sviluppatori competenti, può essere debito pesante se i moduli sono così accoppiati da rendere ogni modifica imprevedibile nelle conseguenze.

Lo spettro: dal micro al sistemico

Il debito tecnico non è un fenomeno uniforme. Si manifesta su livelli diversi con impatti diversi, e confonderli porta a intervenire nel posto sbagliato.

Al livello più granulare c'è il debito misurabile: ogni piccola modifica richiede più tempo del previsto perché le funzioni hanno troppe responsabilità, i livelli di annidamento sono profondi, i comportamenti si sovrappongono. In termini tecnici si parla di complessità ciclomatica elevata; in termini operativi significa che lo stesso cambiamento che altrove richiede un'ora, qui richiede un giorno, perché è difficile capire dove mettere mano senza rompere altro. Strumenti come SonarQube lo tracciano continuamente. Il vantaggio di questa forma di debito è che è visibile e quantificabile: si può misurare il trend nel tempo e intervenire prima che si sedimenti.

Un livello sopra c'è il debito da violazione sistematica dei coding standard. Non lo stile personale di un singolo sviluppatore; quello è preferenza, non debito. Il problema è la mancanza di convenzioni condivise che si è sedimentata nel tempo: ogni sviluppatore che entra nell'area deve ricostruire il contesto da zero perché le regole implicite cambiano da file a file. Il costo non è tecnico ma cognitivo: aumenta il tempo di lettura, riduce la capacità di ragionamento condiviso, rallenta la revisione del codice.

Al livello dei moduli compare il debito da accoppiamento eccessivo: un componente che dipende da troppi altri, o che viene chiamato da troppe parti del sistema, diventa un collo di bottiglia architetturale. In pratica, ogni modifica su quel modulo obbliga a tracciare a mano tutte le dipendenze coinvolte: qual è l'impatto? Chi altri potrebbe rompersi? Questo è il motivo per cui alcune aree del sistema nessuno vuole toccare, le release devono essere sincronizzate e i test di integrazione diventano fragili perché le superfici di contatto si moltiplicano.

Al livello più alto c'è il debito architetturale: le astrazioni fondamentali del sistema non riflettono più come funziona il business. I bounded context sono stati disegnati quando l'azienda aveva un terzo dei processi attuali; la struttura dei dati modella flussi che non esistono più. Questo è il debito più costoso e il più difficile da vedere, perché non si manifesta come errore nel codice ma come resistenza diffusa a qualunque cambiamento strategico.

Cosa non è debito tecnico

Usare il termine in modo impreciso produce conversazioni tecniche che non portano da nessuna parte. Le categorie che vengono confuse con il debito sono quattro, ordinate per tipo di vincolo:

  • Bug: un bug è un errore di comportamento; il sistema fa qualcosa di sbagliato. Il debito tecnico è un vincolo strutturale; il sistema funziona, ma è difficile da modificare. La distinzione è concettuale, non solo terminologica: risolverli richiede approcci diversi, priorità diverse e spesso team diversi. Un sistema con molto debito genera più bug, perché le dipendenze nascoste moltiplicano gli effetti collaterali; tuttavia bug e debito restano fenomeni separati.
  • Feature mancante: non implementare una funzionalità è una scelta di prodotto, non un problema architetturale. Il debito tecnico riguarda la struttura del sistema che esiste, non le decisioni su cosa costruire.
  • Tecnologia "vecchia": l'età di una tecnologia è irrilevante. PostgreSQL su un server del 2012 non è debito se il sistema è modificabile e stabile. Java su un servizio critico non è debito se il team ha ownership e velocità di delivery adeguata. Il debito nasce quando quella tecnologia blocca l'evoluzione: impedisce l'integrazione con sistemi nuovi, richiede competenze sempre più rare, o introduce vincoli che aumentano il costo di ogni cambiamento.
  • Stile personale: se un collega nomina le variabili diversamente da come le nominano gli altri, non è debito. È preferenza. Diventa debito solo quando viola in modo sistematico i coding standard condivisi dal team, perché a quel punto aumenta il carico cognitivo collettivo in modo misurabile e reale.

Il principio che unifica queste distinzioni è già implicito nel test iniziale: il debito è sempre relazionale. Non esiste nel codice in assoluto, ma nel rapporto tra il codice e il cambiamento che il sistema deve sostenere oggi. Questo è il motivo per cui lo stesso modulo può essere debito per un team e non per un altro, o debito a trenta persone e non a sei.

I quattro tipi di Fowler

Martin Fowler ha descritto il debito attraverso un quadrante che incrocia due assi: deliberato/incidentale e sconsiderato/prudente. Il dettaglio è nel suo Technical Debt Quadrant; qui interessa la distinzione operativa che il quadrante rende possibile.

Il debito deliberato e prudente: "facciamo così adesso, sappiamo che dovremo riscrivere". È uno strumento legittimo. Si sceglie consapevolmente uno scorciatoio in un momento specifico, con l'intenzione di ripagarlo quando il contesto lo permette. Il debito incidentale prudente è il contrario: si scopre solo dopo che una decisione presa in buona fede era sbagliata. Accade in ogni sistema che cresce; è il debito della conoscenza che matura, non dell'incompetenza.

La distinzione che conta nella pratica non è tra deliberato e incidentale. Il problema reale è il debito sconsiderato: quello accumulato senza consapevolezza, che si stratifica finché nessuno riesce più a stimare il costo di una modifica. È questa forma che paralizza i team, non le altre.

Perché è difficile vederlo

Il debito tecnico non compare nei report, non ha una voce in bilancio, non genera notifiche. Si manifesta come rumore di fondo: stime che saltano sistematicamente, bug che tornano dopo essere stati risolti, sviluppatori che dicono "quella parte è meglio non toccarla". Quando questi segnali sono leggibili con chiarezza, il debito è già radicato da mesi.

C'è un meccanismo organizzativo che aggrava la situazione. Chi accumula debito spesso non è chi lo paga: il team che prende la scorciatoia per rispettare la scadenza di marzo non è lo stesso che farà manutenzione due anni dopo. Questo disallineamento strutturale non è un difetto morale delle organizzazioni; è una conseguenza naturale di come vengono distribuite responsabilità e incentivi nel tempo. Le organizzazioni ottimizzano per il breve termine perché i costi del debito sono differiti e difficili da attribuire.

Il meccanismo psicologico è altrettanto concreto. Il debito si accumula gradualmente, in incrementi che sembrano sempre gestibili. Ogni singola decisione che lo crea appare ragionevole nel contesto in cui viene presa: c'è fretta, il contesto non è completamente chiaro, la soluzione semplice funziona adesso. Il problema non è la singola decisione, ma l'assenza di un momento dedicato a misurare l'effetto cumulativo di tutte le decisioni prese in condizioni analoghe. Le organizzazioni che gestiscono bene il debito non sono quelle che evitano ogni scorciatoia, ma quelle che si fermano periodicamente a chiedersi dove sono arrivate.

Quando il debito è già al lavoro, i segnali si distribuiscono su tre livelli:

Quanto costa

I dati aggregati sono coerenti: secondo McKinsey, il 40% del patrimonio tecnologico di un'azienda tipica è composto da debito tecnico non gestito, con un impatto diretto del 10-20% sul budget destinato a nuovi prodotti. Gartner stima che gli sviluppatori perdano il 25% del loro tempo produttivo a causa di complessità accumulata. Sono stime su campioni ampi; nella pratica il delta varia molto in base all'area del sistema e alla fase della crescita.

Il numero più utile non è quello aggregato ma quello operativo: quanto tempo serve per fare una modifica piccola in un'area che conosci bene? Se la risposta normale dovrebbe essere "qualche ora", e la risposta effettiva è "una settimana perché bisogna capire come si propagano gli effetti", quel delta si moltiplica su ogni cambiamento futuro in quell'area. Il costo del prossimo cambiamento specifico è sempre più significativo di qualsiasi stima sul debito totale.

La ragione per cui il rallentamento peggiora nel tempo è che la relazione tra complessità e velocità non è lineare: è esponenziale. Aggiungere complessità a un sistema già complesso non costa il doppio; costa molto di più, perché ogni nuovo elemento interagisce con tutti gli elementi esistenti. Aggiungere sviluppatori a un sistema in questo stato non aiuta: aumenta la coordinazione necessaria prima ancora di aumentare la capacità di output. Quando il costo di aggiungere una feature supera il suo valore di business, il debito ha smesso di essere un problema tecnico ed è diventato un vincolo strategico.

Cosa fare concretamente

Tre azioni specifiche per chi vuole smettere di stimare e iniziare a misurare.

  1. Misura cycle time e bug rate nelle ultime 4-8 settimane: non stimare, misurare. Il cycle time è il tempo dal primo commit al deploy in produzione; il bug rate è il numero di incidenti o regressioni per sprint. Se non hai questi dati, chiedi al team quanto tempo ha richiesto l'ultima modifica non banale in ciascuna area critica del sistema. I numeri che emergono sono già un proxy affidabile.
  2. Identifica l'area con più delta tra sforzo atteso e sforzo reale: quella è quasi sempre dove sta il collo di bottiglia. Non il modulo più grande, non quello con il codice più vecchio: quello dove le stime sbagliano di più, dove le pull request restano aperte più a lungo, dove ogni rilascio porta ansia. In quella zona il debito paga già un affitto elevato ogni settimana.
  3. Porta un numero concreto alla prossima conversazione con il management: "questa integrazione costa X settimane invece di Y perché attraversa questi 3 moduli accoppiati". Il costo del prossimo cambiamento specifico è sempre più convincente di qualsiasi stima aggregata sul debito totale. Il Consistency Impact Calculator aiuta a strutturare questa misurazione e a tradurla in un argomento che il management sa leggere.

La prospettiva QMates

Il punto di partenza è misurare gli effetti: cycle time (il tempo dal primo commit al deploy in produzione) e bug rate. Un cycle time che cresce trimestre su trimestre senza un corrispondente aumento nella complessità delle feature è quasi sempre debito al lavoro. Un bug rate in salita mentre la velocità di sviluppo rallenta indica che il sistema resiste ai cambiamenti. Quando le due metriche peggiorano insieme, il debito non è più un'ipotesi; è un dato operativo. Chi vuole iniziare subito può usare il Consistency Impact Calculator per strutturare la prima misurazione.

Le metriche da sole non dicono dove intervenire. Il metodo che usiamo è uno zoom progressivo: si parte dall'effetto aggregato e si scende verso il collo di bottiglia specifico. In alcune organizzazioni il blocco sta nel micro: funzioni con complessità cognitiva fuori controllo, moduli senza test che nessuno tocca. In altre sta nel macro: confini tra team che non riflettono più i flussi di business, architetture disegnate per un'organizzazione che non esiste più. Il livello in cui si trova il collo di bottiglia non è prevedibile a priori; dipende dalla storia di quella specifica organizzazione e da come architettura, team e business hanno smesso di evolvere insieme.

Quello che troviamo quasi sempre non è codice scritto male da sviluppatori incompetenti. Sono architetture disegnate in un momento preciso della crescita che non riflettono più come funziona il business oggi: confini di modulo tracciati quando il team aveva sei persone e oggi ne ha venti, strutture dati che modellano processi cambiati tre volte nel frattempo, astrazioni che erano giuste e ora generano solo workaround. Gli sviluppatori sono competenti; il sistema ha smesso di essere coerente con l'organizzazione che lo produce.

Per questo non entriamo con una lista di refactoring da fare. Mappiamo prima dove il debito blocca il flusso di valore, poi interveniamo lì. Un intervento nel modulo sbagliato, anche eseguito bene, non sposta la velocità di delivery. Questo è il principio che guida il nostro approccio al refactoring strategico e che si collega al Consistency Model: la domanda non è "quanto debito abbiamo?" ma "il sistema è ancora coerente con l'organizzazione che lo produce?"

La domanda sbagliata rimane quella dell'inizio: da quanto tempo questo codice è qui. La domanda operativa, quella che serve davvero, è un'altra: quanto costerà il prossimo cambiamento che devi fare? Se non hai una risposta, hai già trovato il punto da cui partire.

Raccontaci dove sei bloccato

Prototipo fragile, legacy pesante o delivery imprevedibile – partiamo da lì