Debito TecnicoSoftware DeliveryArchitettura

Come misurare il debito tecnico: dall'architettura al flusso di lavoro

Misurare il debito tecnico non significa installare SonarQube. Significa capire dove il sistema oppone resistenza ai cambiamenti che il business richiede. Una guida operativa alle metriche che contano — e a quelle che distraggono.

QMates· Software Advisory14 aprile 202610 min

Perché la misura sbagliata è peggio di nessuna misura

La tentazione più comune quando si vuole misurare il debito tecnico è installare uno strumento di analisi statica, ottenere un punteggio di qualità del codice e usare quel numero come proxy del debito. Il problema è che questo numero misura la complessità del codice, non il costo che quella complessità ha sulla capacità di modificare il sistema.

Un modulo con complessità ciclomatica elevata in un'area che non cambia mai non è un problema operativo. Un modulo con complessità media in un'area che cambia ogni sprint, che è attraversata da ogni nuova feature e da cui dipendono dieci altri moduli, è un blocco. La differenza non sta nel numero che lo strumento produce; sta nella frequenza di modifica e nel peso delle dipendenze.

Misurare il debito tecnico significa rispondere a una domanda specifica: quanto costa modificare questo sistema, e dove costa di più rispetto a quanto dovrebbe? Le metriche che rispondono a questa domanda sono diverse da quelle che misurano la qualità statica del codice. Questa guida si concentra sulle prime.

Le tre metriche operative

Non esistono decine di metriche ugualmente rilevanti per misurare il debito. Ne esistono tre che, lette insieme, danno una visione sufficientemente precisa per prendere decisioni:

Il cycle time è il tempo da quando un'attività viene presa in carico a quando arriva in produzione — il tempo di attraversamento del flusso di lavoro attivo. Non include il tempo di analisi e stima — quello è un problema di processo — ma il tempo effettivo di implementazione, review, test e rilascio. È distinto dal Lead Time for Changes DORA (dal primo commit al deploy in produzione), che include anche il tempo che un commit trascorre in coda prima che il lavoro inizi. In questo articolo usiamo cycle time nel senso Lean: la misura del tempo di attraversamento del flusso attivo, che è la metrica più sensibile per rilevare resistenza interna al sistema. Un cycle time che cresce trimestre su trimestre senza un corrispondente aumento nella complessità delle feature è il segnale più affidabile di debito al lavoro. La ragione è che il debito tecnico si manifesta esattamente lì: ogni modifica richiede più tempo perché le dipendenze nascoste moltiplicano il lavoro necessario, i test falliscono in modo imprevedibile, la revisione richiede di tracciare l'impatto su parti del sistema che non dovrebbero essere coinvolte.

Il bug rate normalizzato è il numero di incidenti e regressioni per unità di feature rilasciata, non il numero assoluto. Un team che rilascia il doppio delle feature può avere il doppio dei bug senza che questo sia un segnale di debito. Il segnale è quando il bug rate per feature cresce: ogni nuova funzionalità genera più problemi delle precedenti, perché il sistema è sempre meno prevedibile nelle sue reazioni ai cambiamenti. Questa metrica è particolarmente utile per localizzare il debito: il bug rate più alto indica quasi sempre l'area del sistema con più accoppiamento nascosto.

Il cost of change è il costo reale di una modifica specifica, calcolato non solo sul tempo di sviluppo ma sull'intero ciclo: analisi dell'impatto, coordinamento cross-team, review approfondita, test manuali compensativi, coordinamento del deploy, verifica post-rilascio. Questa metrica non è automatizzabile: va costruita su casi concreti. Ma è la più direttamente traducibile in un argomento comprensibile al management. "Questa modifica, che avrebbe dovuto richiedere tre giorni, ne ha richiesti quindici perché attraversava questi quattro moduli dipendenti" è un numero specifico su un caso specifico. È più convincente di qualsiasi stima aggregata.

Il Consistency Model: la dimensione che le metriche locali non vedono

Le tre metriche operative misurano il costo del debito nel presente. Non misurano la causa strutturale che lo genera. Per questo serve uno strumento di analisi diverso: il Consistency Model.

Il Consistency Model misura il grado di coerenza tra tre dimensioni che in un sistema sano evolvono insieme: l'architettura software, la struttura dei team che la sviluppano, e i processi di business che deve supportare. Quando queste tre dimensioni sono coerenti, il sistema è modificabile: ogni team ha ownership chiara sulla propria area, i confini architetturali riflettono i confini di responsabilità, i processi di business trovano una rappresentazione diretta nei moduli software.

Quando le tre dimensioni si disallineano — e si disallineano sempre durante la crescita — il debito diventa sistemico. Il segnale specifico del disallineamento non è la qualità del codice; è che le modifiche richieste dal business attraversano sistematicamente confini architetturali che non dovrebbero attraversare.

In pratica: un'organizzazione che ha ristrutturato i team ma non l'architettura si trova con team che devono coordinarsi su ogni modifica perché il codice non riflette la nuova struttura. Un'organizzazione che ha cambiato i processi di business ma non i moduli software si trova con bounded context che modellano flussi che non esistono più, e workaround ovunque per gestire quelli nuovi. Il debito non è nei singoli componenti; è nel gap tra come il sistema è strutturato e come l'organizzazione funziona oggi.

Come misurare il disallineamento

Il Consistency Model non richiede strumenti sofisticati per essere applicato. Richiede le risposte a un insieme di domande strutturate su tre livelli.

Al livello architettura-team: ogni modifica richiesta dal business viene gestita da un singolo team in autonomia, o richiede coordinazione tra più team? Se la risposta è "spesso più team", dove si trovano questi confini? I team che devono coordinarsi di più su ogni feature sono quasi sempre quelli i cui confini architetturali non riflettono i confini di responsabilità. I team che si bloccano a vicenda non hanno un problema di comunicazione; hanno un problema di architettura.

Al livello architettura-processo: i bounded context del sistema riflettono i flussi di business attuali? O ci sono aree dove ogni nuovo processo richiede di lavorare su tre-quattro moduli diversi perché le astrazioni sono state disegnate per un'organizzazione che non esiste più? Un test semplice: prendi un processo di business che è cambiato negli ultimi dodici mesi. Quanti moduli del software hanno richiesto modifiche per supportarlo? Se la risposta è più di due o tre, il sistema non riflette più la struttura del business.

Al livello team-processo: i confini di responsabilità dei team riflettono i flussi di valore verso il cliente? O i team sono organizzati attorno a tecnologie o componenti invece che attorno a prodotti o servizi? Un team che possiede il database ma non l'API che lo espone crea dipendenze strutturali che rallentano qualsiasi modifica al dato.

Dal dato alla decisione

Misurare il debito non è un obiettivo in sé. È il prerequisito per prendere decisioni su dove e come intervenire. Le metriche operative e il Consistency Model servono a rispondere a due domande diverse ma complementari.

Le metriche operative rispondono a: quanto debito stiamo pagando adesso, e dove? Il cycle time indica quanto il sistema rallenta la delivery. Il bug rate indica dove il sistema è meno prevedibile. Il cost of change quantifica il costo concreto su casi specifici. Insieme, indicano dove la pressione è più alta e dove l'intervento avrebbe il ritorno maggiore.

Il Consistency Model risponde a: perché il debito si accumula in quelle aree specifiche? Capire il disallineamento strutturale è necessario per intervenire sulla causa, non solo sull'effetto. Un refactoring che riduce la complessità ciclomatica senza toccare i confini architetturali risolve un sintomo ma non la causa: il debito tornerà, perché le condizioni che lo hanno generato sono ancora presenti.

Il Consistency Impact Calculator è lo strumento che usiamo per strutturare questa analisi in modo che produca un output comprensibile al management: non "abbiamo tanto debito" ma "il costo del prossimo cambiamento in questa area è X, perché attraversa Y moduli non allineati, e potrebbe essere ridotto a Z con un intervento specifico".

Le metriche che distraggono

Non tutte le metriche disponibili sono utili per misurare il debito. Alcune sono più facili da raccogliere ma meno informative; usarle porta a ottimizzare il numero invece del problema sottostante.

La copertura dei test è la più comune. Un'alta copertura non implica un basso debito: si possono avere test su ogni funzione e avere un'architettura così accoppiata da rendere ogni modifica un rischio. La copertura è un prerequisito per poter fare refactoring in sicurezza, non una misura del debito da eliminare.

La complessità ciclomatica assoluta è un'altra metrica fuorviante se usata senza contesto. Come discusso, la complessità che conta non è quella statica del codice ma quella operativa: quanto costa modificare quella parte del sistema con la frequenza con cui va modificata. Un modulo con alta complessità che non cambia mai non è un problema urgente.

Il numero di ticket aperti non è una metrica di debito: è una metrica di carico. Un backlog lungo può riflettere prioritizzazione, non debito. Un backlog che cresce in specifiche aree del sistema ogni sprint, invece, è un segnale indiretto di debito: quella zona è meno gestibile del resto.

Cosa fare concretamente

  1. Strumenta il cycle time per area del sistema: non il cycle time medio globale, che è troppo aggregato per essere utile. Segmenta per area del codebase o per tipo di modifica. Il confronto tra aree rivela quasi sempre pattern che l'analisi aggregata nasconde: un'area che è quattro volte più lenta delle altre è quasi certamente un collo di bottiglia architetturale.
  2. Calcola il cost of change su tre modifiche recenti complesse: scegli tre modifiche che il team ha percepito come più difficili del previsto. Ricostruisci il costo reale: tempo di analisi dell'impatto, coordinamento cross-team, review, test aggiuntivi, coordinamento del deploy. La somma di questi costi, confrontata con la stima originale, produce il delta che misura il costo del debito su quei casi specifici.
  3. Mappa il Consistency Model in un'ora: raduna il tech lead e i responsabili di due-tre team. Rispondi a questa domanda per ogni modifica strategica degli ultimi sei mesi: quanti team hanno dovuto coordinarsi? Quanti moduli del sistema hanno richiesto modifiche? Dove i confini architetturali non riflettono i confini di responsabilità? Il risultato non sarà un'analisi completa, ma sarà sufficiente per identificare le aree con il disallineamento più marcato.

La prospettiva QMates

Il punto di partenza della nostra analisi non è il codice ma il flusso: tracciamo come le modifiche si propagano attraverso il sistema, dove si bloccano, quanto tempo impiegano a completarsi. Le metriche che raccogliamo non sono metriche di qualità statica; sono metriche di attrito dinamico. La differenza è che l'attrito dinamico è direttamente correlato al costo di delivery, mentre la qualità statica del codice non lo è necessariamente.

Quello che troviamo quasi sempre è che il debito più costoso non sta dove il codice è più complesso. Sta dove l'architettura e l'organizzazione dei team hanno smesso di evolvere insieme. Può essere un bounded context che serviva tre clienti e oggi deve servire cento con processi completamente diversi. Può essere un team che ha cambiato composizione e responsabilità ma lavora ancora su un'architettura disegnata per la composizione precedente. La misura del debito che conta è sempre relazionale: non quanto è complesso il codice, ma quanto è costoso modificarlo nel contesto dell'organizzazione che deve farlo.

Il quinto articolo di questa serie si occupa del passo successivo: come ridurre il debito tecnico con strategie che funzionano davvero, senza bloccare la delivery durante l'intervento.

Raccontaci dove sei bloccato

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