Automatizzare la revisione tecnica del codice ereditato: dalla paura all'analisi sistematica
Ogni volta che subentra un consulente su un progetto PHP legacy - e nella mia carriera è successo almeno quaranta volte - la prima settimana è un misto di disorientamento e ansia. Apri il repository, vedi 200 file PHP senza struttura riconoscibile, nomi di variabili in dialetto del programmatore precedente, zero test, zero documentazione, un .env con credenziali di tre ambienti diversi commentate a caso, e un README.md che dice "install with composer". La tentazione è di due tipi opposti e ugualmente pericolosi: o "buttiamo via tutto e riscriviamo" (il disastro economico che ho descritto nel mio articolo sullo Strangler Fig), o "non tocchiamo nulla e facciamo solo patch puntuali" (il che perpetua il debito tecnico e non risolve i problemi strutturali).
La terza via - quella che pratico da anni - è l'audit tecnico sistematico: un processo in cinque fasi che produce un report oggettivo sulla salute della codebase, identifica i rischi critici, e fornisce al management una roadmap di priorità basata su dati misurabili, non su sensazioni. Il processo richiede 3-5 giorni lavorativi e il deliverable è un documento di 15-20 pagine che diventa il piano di lavoro per i mesi successivi. Ho usato questo processo su ogni progetto legacy che ho ereditato, e in ogni caso ha trasformato la domanda iniziale ("quanto è messa male questa codebase?") in una risposta quantificata ("ci sono 14 rischi critici, 23 rischi medi, e una roadmap di 6 mesi per portare la codebase a un livello di manutenibilità accettabile").
Fase 1: analisi statica con PHPStan - i bug che il compilatore non trova
Il primo strumento che eseguo su qualsiasi codebase PHP è PHPStan al livello massimo (level: max o almeno level: 6). PHPStan analizza il codice senza eseguirlo e identifica errori di tipo, chiamate a metodi inesistenti, accesso a proprietà non dichiarate, parametri mancanti e return type inconsistenti. Su una codebase PHP legacy senza type hint, PHPStan al livello 6 produce tipicamente 200-500 errori - ciascuno dei quali è un potenziale bug in produzione che non è stato ancora scoperto perché il percorso di esecuzione che lo attiva non è stato ancora raggiunto da un utente.
Non tutti gli errori PHPStan hanno la stessa gravità. Il primo passaggio dopo l'esecuzione è la classificazione: gli errori di tipo Call to method on mixed sono quasi sempre falsi positivi su codebase senza type hint e possono essere ignorati nella prima fase. Gli errori di tipo Undefined variable $xyz o Call to an undefined method sono bug reali che probabilmente causano errori 500 in produzione in determinati percorsi di esecuzione. Gli errori di tipo Parameter #2 expects int, string given sono bug di tipo che producono comportamenti errati senza generare errori visibili - i più pericolosi perché nessuno sa che ci sono.
La metrica che estraggo da PHPStan per il report è il rapporto errori/file: un codebase con 0,5 errori per file (100 errori su 200 file) è in condizioni mediocri ma gestibili; un codebase con 2+ errori per file è in condizioni critiche e richiede un intervento strutturale prima di qualsiasi feature development. Nel mio profilo professionale trovi il dettaglio dell'esperienza che porto in questi assessment - e la regola che insegno ai team è: PHPStan non va usato per "rendere il codice perfetto", ma per mappare i rischi e dare priorità agli interventi.
Fase 2: complessità ciclomatica e metriche di manutenibilità
La seconda analisi usa PHP Metrics per misurare la complessità strutturale del codice: complessità ciclomatica (quanti percorsi di esecuzione diversi ha un metodo), coupling tra classi (quante classi dipendono da quante altre classi), e un indice di manutenibilità derivato che combina complessità, dimensione e coupling in un punteggio sintetico.
La complessità ciclomatica è la metrica che uso più spesso per identificare i "punti caldi" del codebase - i metodi così complessi che nessuno osa toccarli perché ogni modifica può introdurre una regressione imprevedibile. Un metodo con complessità ciclomatica superiore a 20 ha più di 20 percorsi di esecuzione diversi: testarlo completamente richiederebbe almeno 20 test case, e comprenderlo a fondo richiede di tracciare mentalmente 20 combinazioni di condizioni. I metodi che trovo più frequentemente sopra la soglia nei progetti legacy sono: i controller "dio" che gestiscono l'intera logica di business di una feature in un singolo metodo di 300 righe, i parser di file CSV/XML con 15 livelli di if annidati per gestire tutti i formati storici del file, e le funzioni di calcolo prezzi/sconti con regole di business accumulate negli anni senza ristrutturazione.
Nel report, presento la distribuzione della complessità ciclomatica come un istogramma: quanti metodi sono nella fascia 1-5 (semplici, OK), quanti nella fascia 6-10 (complessi, monitorare), quanti nella fascia 11-20 (molto complessi, candidati al refactoring), e quanti sopra 20 (critici, rischio alto). Il management non ha bisogno di capire cosa sia la complessità ciclomatica - ha bisogno di sapere che "12 metodi nel vostro sistema sono così complessi che ogni modifica ha una probabilità significativa di introdurre un bug, e questi 12 metodi sono responsabili del 40% dei bug in produzione dell'ultimo anno."
Fase 3: mappa delle dipendenze esterne e rischio supply chain
La terza analisi esegue composer audit per verificare le vulnerabilità note nelle dipendenze, composer outdated per identificare i pacchetti significativamente obsoleti, e un'analisi manuale dei pacchetti abbandonati (ultimo commit da più di 2 anni, nessun maintainer attivo). Per una codebase legacy con 60-80 pacchetti Composer, è comune trovare 3-5 pacchetti con vulnerabilità note (di cui 1-2 critiche), 15-20 pacchetti con aggiornamenti major disponibili, e 5-10 pacchetti effettivamente abbandonati che devono essere sostituiti o forkati.
La valutazione del rischio supply chain è il componente dell'audit che il management comprende meglio, perché si traduce direttamente in un rischio di compliance: "il vostro sistema usa una libreria con una vulnerabilità critica nota (CVE-2024-XXXX) che permette l'esecuzione remota di codice. Finché non aggiornate il pacchetto, qualsiasi attaccante che conosce questa CVE può compromettere il vostro server." Questo tipo di rischio ha un impatto legale (GDPR, NIS2) e assicurativo che il management capisce immediatamente. Ho descritto le misure complete di gestione della supply chain PHP nel mio articolo sulla sicurezza della supply chain con Composer.
Fase 4: copertura dei test e debito di testing
La quarta analisi misura la copertura dei test esistenti - se esistono. Nella maggioranza dei progetti legacy che eredito (il 70-80%), la suite di test è in uno di tre stati: inesistente (zero file di test), vestigiale (3-5 test scritti durante lo sviluppo iniziale e mai aggiornati, la metà dei quali non passa più), o parziale ma non integrata (una suite di test che non viene eseguita in CI/CD e che non è stata aggiornata da mesi).
La metrica che presento nel report non è la copertura percentuale (che su un codebase senza test è ovviamente 0-5%), ma una stima del costo di copertura minima - quante ore di lavoro servirebbero per portare le funzionalità critiche del sistema a una copertura di test sufficiente per abilitare il refactoring sicuro. Per una codebase di 30.000 righe con 0% di copertura, la stima tipica è 80-120 ore per raggiungere il 40% di copertura sulle funzionalità critiche - un investimento significativo ma necessario come prerequisito per qualsiasi intervento di modernizzazione. Ho descritto le strategie per l'introduzione graduale di test nei codebase legacy nel mio articolo sui test automatici senza riscrittura.
Fase 5: interviste al team e conoscenza tacita
L'ultima fase dell'audit non è tecnica - è umana. Intervisto ogni membro del team che lavora sul progetto (sviluppatori, QA, product owner, utenti chiave) con una serie di domande standardizzate: "Quale parte del sistema ti spaventa di più quando devi modificarla?", "Quale bug si ripresenta più spesso?", "Cosa avresti voluto che il developer precedente avesse documentato?", "Se potessi riscrivere una sola cosa, cosa sceglieresti?" Le risposte a queste domande rivelano la conoscenza tacita che nessun tool di analisi statica può catturare: i workaround non documentati ("quando fai X, devi sempre fare anche Y altrimenti si rompe Z"), le aree del codice che nessuno osa toccare perché l'unica persona che le capiva non lavora più in azienda, e le priorità reali del team che possono essere diverse dalle priorità che il management dichiara.
I numeri reali: cosa trovo mediamente nei 5 giorni di audit
Per dare un'idea concreta di cosa emerge da un audit tipico, condivido i dati aggregati degli ultimi 8 audit tecnici che ho condotto su codebase PHP legacy di clienti PMI nel 2024-2025. La dimensione media delle codebase era di 42.000 righe di codice PHP su 280 file, con 65 pacchetti Composer, un'età media del progetto di 5,2 anni, e un numero di sviluppatori che avevano lavorato sul progetto nel tempo tra 3 e 7.
I risultati medi dell'analisi statica PHPStan (livello 6) erano: 340 errori su 280 file (rapporto 1,2 errori/file), di cui 45 errori di tipo "undefined method or property" (bug reali), 120 errori di tipo mismatch (potenziali bug), e 175 errori di tipo "mixed" (rumore da assenza di type hint). La complessità ciclomatica media era di 8,4 per metodo, con 15 metodi sopra la soglia di 20 (i candidati al refactoring urgente) e 3 metodi sopra 40 (i mostri che nessuno osa toccare - in un caso, un metodo di 680 righe con complessità ciclomatica 67 che gestiva l'intero flusso di checkout di un e-commerce in un singolo if-else annidato su 12 livelli).
Le dipendenze Composer mostravano mediamente 3,4 vulnerabilità note (di cui 0,8 critiche), 22 pacchetti con aggiornamento major disponibile, e 4,2 pacchetti abbandonati (ultimo commit da più di 2 anni). La copertura dei test era in media del 7% - con 4 dei 8 progetti a 0% e gli altri 4 tra il 12% e il 28%. Nessuno degli 8 progetti aveva una pipeline CI/CD che eseguisse i test automaticamente - il deployment era manuale via SSH/FTP in 6 casi e via webhook Git senza test in 2 casi.
Il dato più significativo per il management è la stima del debito tecnico in ore di lavoro. Usando il metodo di calcolo basato sulle metriche (ogni errore PHPStan critico = 30 minuti di fix, ogni metodo sopra la soglia di complessità = 4 ore di refactoring, ogni vulnerabilità critica nelle dipendenze = 2 ore di aggiornamento e test), il debito tecnico medio degli 8 progetti era di 380 ore di lavoro - circa 2 mesi e mezzo di lavoro full-time di un developer senior. Questo numero, presentato al management insieme al costo orario del developer e al costo stimato dei bug in produzione che il debito tecnico genera (dal tracking degli incidenti dell'ultimo anno), produce la base dati per la decisione di investimento: "costa meno correggere il debito tecnico adesso (380 ore = X euro) o continuare a pagare il costo dei bug in produzione (Y ore al mese di debug e hotfix × 12 mesi = Z euro)."
In tutti e 8 i casi, il management ha scelto di investire nella riduzione del debito tecnico - non perché sono idealisti, ma perché i numeri dimostravano che il costo del debito era superiore al costo della correzione entro 6-12 mesi. L'audit tecnico non è un esercizio accademico - è un business case quantificato che giustifica l'investimento nella qualità del codice con il linguaggio che il management capisce: costi, rischi e ritorni.
Il deliverable finale dell'audit è un documento strutturato in tre sezioni: un executive summary per il management (3-5 rischi critici con impatto di business, stima del debito tecnico in ore di lavoro, e raccomandazione sulla strategia - refactoring incrementale, riscrittura parziale o manutenzione conservativa), una sezione tecnica per il team (risultati di PHPStan, PHP Metrics e Composer Audit con la lista dei file/metodi/pacchetti da correggere prioritariamente), e una roadmap con milestones a 30, 60 e 90 giorni che definisce cosa fare prima, cosa dopo e cosa rimandare.
Questo processo trasforma il "non sappiamo quanto è messo male" in "sappiamo esattamente dove sono i problemi e quanto costa risolverli" - e questa trasparenza è ciò che permette al management di prendere decisioni informate invece che decisioni basate sulla paura o sull'ottimismo infondato. Se hai ereditato un codebase PHP e non sai da dove cominciare, contattami per un audit tecnico strutturato: in 3-5 giorni produco il report completo con metriche, rischi e roadmap che diventa il piano di lavoro condiviso tra team tecnico e management.