Parliamone
// tecnologie.code-quality-metrics

Code Quality Metrics

Complessità ciclomatica, complessità cognitiva, metriche di accoppiamento, modello SQALE del debito tecnico, metriche di Halstead e indicatori DORA: fondamenti, limiti empirici e criteri per una misurazione orientata alle decisioni ingegneristiche.

Software Architecture

Executive summary

Quando un'organizzazione sviluppa software nel tempo, la capacità di misurare oggettivamente quanto sia difficile mantenere, modificare e comprendere il codice sorgente è determinante per pianificare il lavoro di ingegneria e prevenire il deterioramento della qualità interna. Esistono numerosi indicatori numerici consolidati che quantificano la complessità strutturale del codice, il grado di dipendenza tra i componenti e il costo stimato per sanare i problemi accumulati. Questo articolo analizza i principali di questi indicatori, dai fondativi lavori degli anni Settanta alle metriche di consegna del software degli anni recenti, confrontandone i fondamenti teorici con le evidenze sperimentali, incluse quelle provenienti dalle neuroscienze, per distinguere le misurazioni che guidano decisioni efficaci da quelle che producono una falsa sensazione di controllo. L'analisi mostra che nessuna metrica sintattica cattura pienamente la comprensibilità del codice, e che la loro utilità dipende dall'uso consapevole dei loro limiti piuttosto che dalla cieca applicazione di soglie numeriche.


Background

La misurazione quantitativa della qualità del software ha origine negli anni Settanta, quando la crescente dimensione dei sistemi informatici rese evidente che la complessità del codice non poteva essere gestita esclusivamente attraverso il giudizio soggettivo degli sviluppatori. Thomas McCabe, nel 1976, propose la complessità ciclomatica come misura strutturale basata sulla teoria dei grafi [1]. L'anno successivo, Maurice Halstead pubblicò Elements of Software Science [2], introducendo un sistema di metriche fondato sulla conta degli operatori e degli operandi nel codice sorgente: un tentativo di derivare proprietà quantitative del software con metodi analoghi a quelli delle scienze fisiche. Queste due contribuzioni inaugurarono un filone di ricerca che nei decenni successivi ha prodotto centinaia di indicatori.

Il problema fondamentale non è mai stato la scarsità di metriche disponibili, bensì la difficoltà di stabilire quali misurino effettivamente proprietà rilevanti per le decisioni ingegneristiche. Il teorema di Rice (1953) stabilisce che qualsiasi proprietà semantica non triviale di un programma è indecidibile nel caso generale [3]: di conseguenza, ogni metrica statica è necessariamente un'approssimazione. L'utilità di una metrica dipende dalla sua correlazione con gli esiti che interessa prevedere (difettosità, costo di manutenzione, comprensibilità) e dalla correttezza con cui questa correlazione viene interpretata.

Il contesto contemporaneo è dominato da piattaforme integrate come SonarQube, che aggregano decine di metriche in dashboard e quality gate [4], e dallo standard ISO/IEC 25010 [5], che fornisce un modello di qualità del software articolato in otto caratteristiche misurabili. Accanto alle metriche di qualità statica del codice, il lavoro di Forsgren, Humble e Kim [6] ha introdotto le metriche DORA (DevOps Research and Assessment) come indicatori della performance di consegna del software a livello organizzativo, spostando l'attenzione dalla qualità interna del codice agli esiti del processo di sviluppo. L'analisi che segue esamina ciascun dominio di metrica, ne valuta i fondamenti e ne discute i limiti.


Metriche di complessità strutturale

Complessità ciclomatica di McCabe

La complessità ciclomatica, introdotta da McCabe nel 1976, è la metrica di complessità più longeva e diffusa nell'ingegneria del software [1]. La definizione formale si basa sulla teoria dei grafi: dato il grafo di controllo di flusso $G$ di una funzione, con $e$ archi, $n$ nodi e $p$ componenti connesse, la complessità ciclomatica è:

$$v(G) = e - n + 2p$$

Per un programma strutturato con un singolo punto di ingresso e uscita, $v(G)$ equivale al numero di decisioni condizionali (if, else, case, while, for) più uno. In termini pratici, la metrica conta quanti percorsi linearmente indipendenti esistono attraverso una funzione. Le soglie convenzionali (inferiore a 10 per codice ben strutturato, 11-20 per complessità moderata, superiore a 20 per codice ad alto rischio) derivano dall'analisi empirica di McCabe e dalla validazione condotta dal NIST [1, 7].

La correlazione tra complessità ciclomatica elevata e tasso di difettosità è stata confermata da studi empirici. Tuttavia, la metrica presenta limiti strutturali che ne circoscrivono l'utilità interpretativa. Il primo è l'insensibilità alla profondità del nesting: una sequenza piatta di dieci condizionali indipendenti e tre livelli di condizionali annidati possono avere la stessa complessità ciclomatica, pur essendo radicalmente diverse in termini di comprensibilità. Il secondo è l'indifferenza alla complessità delle espressioni booleane: una singola condizione if (a && b || c) conta come un solo branch, nonostante richieda al programmatore di ragionare su tre condizioni distinte. Il terzo è l'assenza di qualsiasi considerazione semantica: la metrica non distingue tra logica di dominio complessa e semplice dispatching, né tra codice con nomi esplicativi e variabili a singola lettera.

Metriche di Halstead

Il sistema di metriche proposto da Halstead nel 1977 [2] classifica ogni token del codice sorgente come operatore o operando e deriva grandezze composite:

  • Vocabolario: $n = n_1 + n_2$, dove $n_1$ è il numero di operatori distinti e $n_2$ quello degli operandi distinti
  • Lunghezza: $N = N_1 + N_2$, dove $N_1$ e $N_2$ sono le occorrenze totali
  • Volume: $V = N \cdot \log_2(n)$, interpretabile come il contenuto informativo del programma in bit
  • Difficoltà: $D = \frac{n_1}{2} \cdot \frac{N_2}{n_2}$, stima della complessità cognitiva
  • Sforzo: $E = D \cdot V$, stima dell'effort mentale per comprendere il codice

Le validazioni empiriche successive hanno prodotto risultati contrastanti. Uno studio fMRI condotto da Peitek et al. (2021) su 19 programmatori ha analizzato oltre 41 metriche durante la comprensione di frammenti di codice, rilevando che le metriche basate sulla dimensione del vocabolario ($n_1$, $n_2$) si correlano con l'attività delle regioni cerebrali associate alla memoria di lavoro [8]. Tuttavia, le metriche composite di Halstead, difficoltà e sforzo, non mostrano il comportamento monotonico atteso: codice con punteggio di difficoltà più alto non viene necessariamente percepito come più difficile dai programmatori. Lo studio ha concluso che "nessuna classe di metriche statiche analizzate spiega adeguatamente l'attività neuronale osservata" [8], indicando che la comprensibilità è influenzata da fattori che le metriche sintattiche non catturano.

Complessità cognitiva

La complessità cognitiva, formalizzata da G. Ann Campbell nel 2018 [9], è stata progettata specificamente per superare i limiti della complessità ciclomatica. A differenza della complessità ciclomatica, che tratta ogni branch in modo equivalente, la complessità cognitiva introduce tre principi di calcolo differenziati.

Il primo principio è l'incremento per struttura di controllo: ogni struttura di controllo incrementa il conteggio di un'unità. Il secondo principio è il nesting increment: per ogni livello di annidamento, l'incremento aggiuntivo cresce di un'unità, perciò un if dentro un for riceve un incremento di due anziché uno, producendo penalizzazioni crescenti per il codice profondamente annidato. Il terzo principio è l'assenza di incremento per le strutture lineari: una catena piatta di else if non produce incremento di nesting, riflettendo il fatto che una sequenza lineare di condizioni è cognitivamente più semplice di condizioni annidate [9]. Una funzione con tre livelli di nesting e complessità ciclomatica 11 può avere complessità cognitiva superiore a 20, mentre una funzione con dieci condizionali piatti avrebbe complessità cognitiva circa 10.

La validazione empirica della complessità cognitiva è ancora in consolidamento. Lo studio di Munoz et al. (2023), condotto con 27 programmatori utilizzando eye tracking ed EEG, ha rilevato che la complessità cognitiva di SonarSource "devia considerevolmente dalla percezione della complessità da parte dei programmatori e spesso non mostra il comportamento monotonico atteso" [10]. La correlazione tra carico cognitivo misurato EEG e complessità cognitiva di SonarSource è risultata di soli $r_s = 0.513$, mentre la misura di difficoltà di Halstead ha mostrato correlazione superiore ($r_s = 0.901$) con il carico cognitivo elettroencefalografico [10]. Questo risultato non invalida la complessità cognitiva come strumento pratico, ma ne circoscrive la portata: la metrica cattura il nesting, ma non esaurisce i fattori reali della difficoltà di comprensione.


Metriche di accoppiamento

Le metriche di accoppiamento misurano il grado di dipendenza tra i componenti di un sistema software. Robert C. Martin ha formalizzato le metriche di accoppiamento afferente ed efferente a livello di package nel contesto della progettazione orientata agli oggetti [11]. L'accoppiamento afferente ($C_a$) di un package è il numero di classi esterne a quel package che dipendono da classi interne: misura quanti altri componenti dipendono dal package e, di conseguenza, quanti componenti sarebbero impattati da una sua modifica. L'accoppiamento efferente ($C_e$) è il numero di classi esterne al package da cui dipendono le classi interne: misura quante dipendenze il package introduce verso il resto del sistema.

Da queste due misure, Martin deriva due indici compositi di interesse architetturale. L'instabilità è definita come:

$$I = \frac{C_e}{C_a + C_e} \in [0, 1]$$

Un package con $I = 0$ è completamente stabile (nessuna dipendenza efferente, molte dipendenze afferenti): qualsiasi sua modifica impatta molti altri componenti, creando una forte pressione conservativa. Un package con $I = 1$ è completamente instabile: può essere modificato liberamente senza propagare effetti. Il Principio delle Dipendenze Stabili enuncia che ogni package dovrebbe dipendere solo da package più stabili di sé [11].

L'astrazione $A$ è il rapporto tra il numero di classi astratte e interfacce nel package e il numero totale di classi. La distanza dalla sequenza principale è:

$$D = |A + I - 1|$$

Un package con $D = 0$ si trova sulla sequenza principale: o è astratto e stabile (interfacce di dominio), o è concreto e instabile (implementazioni foglia). Un package con $D$ elevata è o concreto e stabile (difficile da modificare ma privo di astrazioni, rigido), o astratto e instabile (pieno di astrazioni ma senza dipendenti, inutile) [11]. Queste metriche sono implementate in strumenti come JDepend (Java), NDepend (.NET) e ArchUnit, e forniscono un linguaggio quantitativo per discussioni architetturali che altrimenti resterebbero esclusivamente qualitative.


Modello SQALE e quantificazione del debito tecnico

Il modello SQALE (Software Quality Assessment based on Lifecycle Expectations), formalizzato da Letouzey e Ilkiewicz nel 2012 [12], rappresenta il tentativo più strutturato di tradurre la metafora del debito tecnico in una misura quantitativa operativa. Il concetto fu introdotto da Ward Cunningham nel 1992 nel suo report sull'esperienza con il sistema WyCash [13]: rilasciare codice che riflette una comprensione immatura del dominio è come contrarre un debito finanziario, accettabile se ripagato tempestivamente tramite refactoring, pericoloso quando si accumula. SQALE si basa sul modello ISO/IEC 25010 e classifica le violazioni rilevate dall'analisi statica in un albero gerarchico di caratteristiche: testabilità, affidabilità, modificabilità, efficienza, sicurezza, manutenibilità, portabilità [5, 12].

Il meccanismo di calcolo opera in tre fasi. Nella prima, ogni regola violata viene associata a un costo di remediation espresso in minuti: il tempo stimato per correggere la singola violazione. Nella seconda, i costi vengono aggregati per caratteristica di qualità. Nella terza, il Technical Debt Ratio viene calcolato come rapporto tra il costo totale di remediation e il costo stimato per riscrivere l'intera applicazione, approssimato da SonarQube come il prodotto tra linee di codice e 30 minuti per linea [4, 12]:

$$\text{Technical Debt Ratio} = \frac{\text{Costo di remediation totale}}{\text{Linee di codice} \times 30\text{ min}} \times 100$$

SonarQube traduce il Technical Debt Ratio in un rating da A a E: A per un rapporto inferiore al 5%, E superiore al 50% [4].

Il limite principale del metodo SQALE risiede nella stima dei costi di remediation, che sono valori predefiniti non calibrati sul contesto del progetto. Correggere una violazione di duplicazione in un modulo ben testato con alta copertura richiede uno sforzo radicalmente diverso dalla stessa correzione in un modulo legacy senza test. Inoltre, SQALE opera esclusivamente a livello di codice sorgente e non cattura il debito tecnico architetturale (accoppiamento eccessivo tra servizi, dipendenze circolari, inconsistenze nei pattern di comunicazione) che in molti contesti rappresenta la quota più significativa del debito complessivo [12].


Metriche DORA per la performance di consegna

Le metriche DORA (DevOps Research and Assessment), introdotte da Forsgren, Humble e Kim in Accelerate (2018) [6] e validate da anni di ricerca empirica su migliaia di organizzazioni, misurano la performance di consegna del software a livello di processo. Le quattro metriche fondamentali sono: Deployment Frequency (con quale frequenza l'organizzazione rilascia in produzione), Lead Time for Changes (il tempo tra il commit e il deployment in produzione), Change Failure Rate (la percentuale di deployment che produce incidenti o richiede rollback) e Time to Restore Service (il tempo medio per ripristinare il servizio in caso di incidente).

Forsgren et al. hanno dimostrato attraverso analisi strutturale delle equazioni che la performance di consegna correla significativamente con la performance organizzativa (produttività, redditività, quota di mercato) [6]. I team classificati come "elite" nel report DORA 2023 raggiungono Deployment Frequency multipla al giorno, Lead Time for Changes inferiore a un'ora, Change Failure Rate inferiore al 5% e Time to Restore inferiore a un'ora [14].

La relazione tra metriche DORA e metriche di qualità del codice è indiretta ma sostanziale. Un'elevata complessità ciclomatica e un alto debito tecnico rendono le modifiche più rischiose e costose, aumentando il Lead Time e il Change Failure Rate. Una suite di test automatizzati efficace riduce il Change Failure Rate e il Time to Restore. Le metriche DORA non sostituiscono le metriche di qualità del codice, ma le contestualizzano: una base di codice con bassa complessità ma Deployment Frequency bassa potrebbe indicare barriere di processo, non di qualità tecnica; viceversa, Deployment Frequency alta con Change Failure Rate elevato suggerisce carenze nei meccanismi di verifica automatizzata [6, 14].


Strumenti di misurazione e soglie operative

SonarQube [4] è la piattaforma di analisi della qualità del codice più diffusa in ambito enterprise. Il meccanismo più influente sul processo di sviluppo è il quality gate: un insieme di condizioni soglia applicate al codice nuovo che determina se una pull request può essere approvata. La strategia "Clean as You Code" di SonarSource applica standard stringenti esclusivamente al codice modificato, consentendo al team di arrestare l'accumulo di debito senza richiedere la bonifica immediata del codice preesistente [4]. Le soglie operative di riferimento per le metriche di complessità sono: complessità ciclomatica massima per funzione di 10 (NIST, codice a basso rischio) o 15-20 (prima di refactorizzazione obbligatoria) [7]; complessità cognitiva massima per funzione di 15 (SonarQube) [4, 9]; distanza dalla sequenza principale $D > 0.5$ come segnale di problemi architetturali [11].

Il rischio principale nell'uso delle metriche come quality gate è la sostituzione del giudizio ingegneristico con il rispetto dei numeri: la legge di Goodhart applicata al codice. Quando la complessità ciclomatica viene usata come soglia di accettazione, gli sviluppatori possono ridurla meccanicamente estraendo funzioni artificiali senza guadagno reale di comprensibilità. Quando la code coverage diventa un obiettivo numerico, la suite di test può crescere in volume senza crescere in efficacia, con test che esercitano percorsi senza verificare la correttezza dei risultati. Le metriche sono strumenti di diagnosi e orientamento, non certificati di qualità: la loro interpretazione richiede sempre contestualizzazione rispetto all'architettura del sistema e all'esperienza del team.


Limiti e problemi aperti

Le evidenze neuroscientifiche più recenti hanno messo in discussione l'assunto fondamentale che sottende tutte le metriche di complessità: la possibilità di predire la difficoltà cognitiva di comprensione del codice a partire da proprietà sintattiche misurabili automaticamente. Lo studio di Munoz et al. (2023) [10] ha rilevato che la correlazione tra frequenza di revisiti a regioni di codice (indicatore comportamentale della difficoltà) e carico cognitivo EEG è molto più forte ($r_s = 0.963$) rispetto a qualsiasi metrica sintattica analizzata. I fattori che determinano la comprensibilità del codice (familiarità con il dominio, qualità dei nomi, coerenza con i pattern architetturali, presenza di documentazione contestuale) non sono catturabili da metriche puramente sintattiche, e questa limitazione è strutturale, non risolvibile attraverso metriche più sofisticate dello stesso tipo.

La complementarità tra metriche diverse è documentata ma poco sfruttata nella pratica. Lo studio di Peitek et al. [8] e quello di Munoz et al. [10] mostrano che metriche di complessità distinte catturano aspetti non sovrapposti della qualità del codice (la dimensione del vocabolario, la profondità del nesting, la frequenza dei revisiti comportamentali) e che la loro combinazione ha potere esplicativo superiore rispetto alle singole metriche prese isolatamente. La sfida è costruire modelli multi-metrica interpretabili (non black box) calibrati su codebase rappresentativi del contesto applicativo specifico. La tendenza prevalente, al contrario, è di affidarsi a indici compositi (maintainability index, SQALE rating) che semplificano la complessità al costo dell'opacità diagnostica.

La misurazione del debito tecnico architetturale rimane un problema parzialmente aperto. Strumenti come ArchUnit (Java), NetArchTest (.NET) e Dependency Cruiser (JavaScript) consentono di esprimere vincoli architetturali come test automatizzati nella pipeline CI, ma non producono una quantificazione del debito analoga a SQALE. Lo sviluppo di modelli quantitativi per il debito tecnico a livello di architettura, che siano calibrabili, interpretabili e comparabili tra progetti, è un'area di ricerca attiva senza ancora un consenso metodologico consolidato.

Le metriche DORA, nonostante la loro validità empirica, presentano difficoltà di misurazione accurata in contesti organizzativi complessi. Il Lead Time for Changes richiede tracciabilità completa dalla modifica al deployment, che presuppone un'integrazione stretta tra sistemi di version control, pipeline CI/CD e sistemi di monitoring. Il Change Failure Rate richiede la definizione consensuale di "failure", che varia significativamente tra team e organizzazioni. In assenza di definizioni precise e di sistemi di tracciabilità adeguati, le metriche DORA diventano autoreferenziali e perdono il valore comparativo che le rende utili [6, 14].


Riferimenti

[1] T. J. McCabe, "A Complexity Measure," IEEE Transactions on Software Engineering, vol. SE-2, no. 4, pp. 308-320, 1976. https://doi.org/10.1109/TSE.1976.233837

[2] M. H. Halstead, Elements of Software Science, Elsevier, 1977.

[3] H. G. Rice, "Classes of Recursively Enumerable Sets and Their Decision Problems," Transactions of the American Mathematical Society, vol. 74, no. 2, 1953. https://doi.org/10.1090/S0002-9947-1953-0053041-6

[4] SonarSource, "SonarQube Server Documentation: Understanding Measures and Metrics," 2025. https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition

[5] ISO/IEC 25010:2023, "Systems and Software Engineering: Systems and Software Quality Requirements and Evaluation (SQuaRE): Product Quality Model," International Organization for Standardization, 2023. https://www.iso.org/standard/35733.html

[6] N. Forsgren, J. Humble, G. Kim, Accelerate: The Science of Lean Software and DevOps, IT Revolution Press, 2018.

[7] A. H. Watson e T. J. McCabe, "Structured Testing: A Testing Methodology Using the Cyclomatic Complexity Metric," NIST Special Publication 500-235, 1996. https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication500-235.pdf

[8] N. Peitek et al., "Program Comprehension and Code Complexity Metrics: An fMRI Study," in Proc. ICSE 2021, IEEE/ACM, 2021. https://doi.org/10.1109/ICSE43902.2021.00056

[9] G. A. Campbell, "Cognitive Complexity: A new way of measuring understandability," SonarSource, 2023. https://www.sonarsource.com/docs/CognitiveComplexity.pdf

[10] G. Hao et al., "On the Accuracy of Code Complexity Metrics: A Neuroscience-Based Guideline for Improvement," Frontiers in Neuroscience, vol. 16, 2023. https://doi.org/10.3389/fnins.2022.1065366

[11] R. C. Martin, Agile Software Development, Principles, Patterns, and Practices, Prentice Hall, 2002.

[12] J.-L. Letouzey e M. Ilkiewicz, "Managing Technical Debt with the SQALE Method," IEEE Software, vol. 29, no. 6, pp. 44-51, 2012. https://doi.org/10.1109/MS.2012.129

[13] W. Cunningham, "The WyCash Portfolio Management System," in OOPSLA '92 Addendum, ACM, 1992. https://c2.com/doc/oopsla92.html

[14] DORA Team, "Accelerate State of DevOps Report 2023," Google Cloud, 2023. https://dora.dev/research/2023/dora-report/

Code Quality Metrics

Raccontaci la situazione. Rispondiamo entro 24 ore nei giorni lavorativi.

Tweaks

Light mode
Atmospheric (glass)
Client logos
Terminal hero