Complessità Ciclomatica Spiegata: Perché è Importante e Come Ridurla
Complessità ciclomatica spiegata: cosa misura, perché predice debito tecnico e incidenti, e come ridurla senza riscrivere da zero.
In questo articolo:
- Cos’è la complessità ciclomatica?
- Perché la complessità ciclomatica predice i problemi
- Come misurarla nel tuo codebase
- Soglie e benchmark
- Come ridurre la complessità ciclomatica
- Conclusione
Cos’è la complessità ciclomatica?
La complessità ciclomatica è una metrica software introdotta da Thomas J. McCabe nel 1976. Misura il numero di percorsi indipendenti attraverso un pezzo di codice. In termini pratici, conta il numero di punti decisionali: istruzioni if, rami else, clausole case, cicli while e for, blocchi catch e operatori ternari. Ognuno aggiunge uno al punteggio di complessità.
Una funzione senza punti decisionali ha una complessità ciclomatica di 1. Una funzione con una singola istruzione if ha una complessità di 2. Una funzione con dieci istruzioni if, condizioni annidate e un ciclo ha una complessità di 12 o superiore.
La metrica è importante perché la complessità del codice è un predittore diretto di due risultati di engineering: il numero di casi di test necessari per ottenere la copertura completa dei rami, e la probabilità che la funzione contenga difetti. Una funzione con complessità ciclomatica 15 richiede almeno 15 casi di test per ottenere la copertura completa.
Perché la complessità ciclomatica predice i problemi
La complessità del codice predice problemi per tre ragioni strutturali.
Testabilità. Ogni percorso indipendente attraverso una funzione deve essere testato. Una funzione con complessità 20 ha bisogno di almeno 20 casi di test per essere completamente coperta. In pratica, i team raramente raggiungono la copertura completa delle funzioni complesse, il che significa che rami di logica non vengono testati e producono comportamenti inaspettati in produzione.
Modificabilità. Quando una funzione ha molti punti decisionali, una modifica a un ramo può influenzare altri rami in modi non ovvi. Il developer che effettua la modifica deve tenere a mente l’intera struttura decisionale per ragionare in modo sicuro sull’effetto della modifica.
Densità dei difetti. La ricerca mostra in modo coerente che le funzioni con complessità ciclomatica più alta contengono più bug per riga di codice. Il 5% più complesso di un codebase tipicamente genera dal 25% al 40% di tutti i difetti in produzione. Questa è la dinamica degli hotspot descritta nelle nostre valutazioni di tech debt solution.
Come misurarla nel tuo codebase
La complessità ciclomatica viene calcolata automaticamente dalla maggior parte degli strumenti di analisi statica. SonarQube la riporta a livello di funzione, classe e modulo e traccia le tendenze nel tempo. CodeClimate e Codacy forniscono funzionalità simili.
L’output più utile non è un numero aggregato ma una lista classificata: le dieci funzioni con la complessità ciclomatica più alta nel codebase, ordinate per una combinazione di punteggio di complessità e frequenza di modifica. Le funzioni che sono sia complesse che frequentemente modificate sono i target a più alto rischio per la remediation.
Integrare il controllo della complessità nella pipeline CI/CD crea un quality gate. Una build che aumenta la complessità massima delle funzioni oltre una soglia definita (tipicamente 15 per il nuovo codice) fallisce automaticamente.
Soglie e benchmark
Le soglie standard per la complessità ciclomatica, originariamente proposte da McCabe e successivamente validate empiricamente, sono:
Da 1 a 10: semplice, facile da capire e testare. Accettabile per la maggior parte del codice. Da 11 a 15: complessità moderata. Dovrebbe essere rivista ma non automaticamente rifattorizzata. Da 16 a 25: alta complessità. Alto rischio di difetti. Il refactoring è raccomandato prima che la funzione venga modificata nuovamente. Sopra 25: complessità molto alta. Priorità di remediation urgente.
In pratica, un codebase ben mantenuto ha una complessità ciclomatica mediana da 3 a 5 e un massimo di 15. Un codebase con debito tecnico significativo avrà più funzioni sopra 20 e una mediana sopra 8. Un codebase cliente che abbiamo valutato aveva la funzione principale a complessità 74, una funzione che aveva accumulato logica di business per sette anni senza refactoring.
Come ridurre la complessità ciclomatica
Ridurre la complessità del codice non richiede di riscrivere la funzione da zero. L’approccio più affidabile è il refactoring Extract Method, applicato in modo incrementale.
Il processo: identifica la funzione più complessa nel codebase. Identifica un blocco di istruzioni logicamente coeso all’interno di quella funzione che potrebbe essere nominato ed estratto. Scrivi test per il comportamento attuale della funzione prima di fare qualsiasi modifica. Estrai il blocco in un nuovo metodo con un nome. Verifica che i test passino ancora. Misura la complessità sia della funzione originale che del metodo estratto.
Applicato ripetutamente, questo processo converte una singola funzione con complessità 30 in cinque funzioni ciascuna con complessità 6, senza cambiare il comportamento osservabile.
La seconda tecnica è la sostituzione dei condizionali annidati con ritorni anticipati (guard clause). Invece di una struttura if-else profondamente annidata, ogni pre-condizione viene controllata all’inizio della funzione e restituisce presto se non soddisfatta.
La terza tecnica è l’introduzione del polimorfismo per sostituire catene complesse di switch o if-else multi-ramo che dispacciano il comportamento in base a un tipo.
Conclusione
La complessità ciclomatica è una delle metriche di qualità del codice più affidabili disponibili per i team di engineering. È oggettiva, automatizzata e direttamente correlata al rischio di difetti, alla testabilità e al costo di manutenzione.
Ridurre la complessità è ottenibile in modo incrementale senza fermare lo sviluppo di funzionalità. La chiave è puntare alle funzioni con complessità più alta nelle aree a più alto rischio del codebase, applicare piccoli cicli di estrazione e test, e integrare soglie di complessità nella pipeline CI per prevenire nuovi accumuli.
Eden Technologies misura la complessità ciclomatica come componente core di ogni valutazione del codebase. È uno dei predittori più coerenti di dove i clienti vedranno problemi di delivery e rischio di incidenti.
Hai un codebase con questi problemi? Parliamo del tuo sistema