Subentro su codebase Laravel senza documentazione: il metodo in 48 ore per capire cosa hai ereditato

Subentro su codebase Laravel senza documentazione: il metodo in 48 ore per capire cosa hai ereditato

C'è un momento preciso, in ogni subentro su un progetto Laravel, in cui l'emergenza tecnica finisce e inizia la parte davvero difficile. Il server è stato recuperato, le credenziali sono state ruotate, l'applicazione risponde, i backup sono verificati. Il titolare tira un sospiro di sollievo e dice: "Bene, ora puoi lavorarci." E ti trovi davanti a 85.000 righe di codice scritte da qualcun altro, senza documentazione, senza test, senza un singolo commento che spieghi perché una certa decisione è stata presa. L'applicazione funziona - nel senso che genera fatturato - ma nessuno sa come. Il precedente sviluppatore non è più raggiungibile, o lo è ma non ha tempo, o lo è ma non ricorda.

Il caso più recente in cui ho applicato il metodo che descrivo in questo articolo è di luglio 2025: una PMI manifatturiera in provincia di Brescia, 60 dipendenti, un gestionale Laravel 9 custom che gestiva ordini, produzione, magazzino e fatturazione elettronica. Lo sviluppatore - un freelance che aveva lavorato al progetto per quattro anni - aveva accettato un'offerta aziendale e aveva dato un mese di preavviso. Ma il "passaggio di consegne" era consistito in un file README.md di 23 righe con le istruzioni per il deploy e la password del database. Nient'altro. Ho descritto la fase di emergenza - recupero accessi, stabilizzazione, stop-the-bleeding - nel mio articolo sul subentro forzato nelle prime 72 ore quando lo sviluppatore non risponde più. Questo articolo copre quello che viene dopo: capire cosa hai ereditato, dove sono i rischi, e cosa dire al titolare entro venerdì.

Cosa puoi davvero capire di una codebase Laravel in 48 ore senza parlare con chi l'ha scritta?

Più di quanto pensi, se usi gli strumenti giusti nell'ordine giusto. Il codice è la documentazione definitiva di se stesso - non mente, non dimentica, non ha la memoria selettiva di uno sviluppatore che "ricorda le cose importanti." Il problema non è la mancanza di informazione. È la mancanza di struttura nell'analisi. Quarantotto ore non bastano per capire tutto, ma bastano per produrre una mappa di rischio che dice al titolare: queste 5 aree sono sicure, queste 3 sono fragili, questa 1 è una bomba a orologeria.

Il metodo che ho raffinato su decine di subentri si divide in quattro fasi da mezza giornata ciascuna. L'ordine non è arbitrario - ogni fase alimenta la successiva.

Fase 1 (ore 0-4): scansione automatica e metriche di base

Prima di aprire un singolo file nel editor, lascio che siano gli strumenti automatici a darmi il quadro d'insieme. Tre comandi che eseguo nei primi trenta minuti su qualunque progetto Laravel:

# Dimensioni e composizione della codebase
cloc app/ routes/ config/ database/ resources/views/ --exclude-dir=vendor
# Output: righe di codice per linguaggio, commenti, file vuoti

# Analisi statica con PHPStan al livello più basso (meno falsi positivi)
vendor/bin/phpstan analyse app/ --level=1 --no-progress --error-format=table

# Dipendenze e vulnerabilità note
composer audit
composer show --tree --no-dev | head -50

PHPStan al livello 1 non cerca la perfezione - cerca i bug ovvi: variabili non definite, tipi incompatibili, metodi chiamati su oggetti potenzialmente null. Su una codebase di 85.000 righe senza type hint, il livello 1 produce già un output significativo senza essere sommerso da falsi positivi. Sul gestionale bresciano, PHPStan al livello 1 ha restituito 342 errori. Di questi, 38 erano in un singolo file: app/Services/InvoiceGenerator.php - 1.200 righe di logica di fatturazione elettronica con variabili non dichiarate, condizioni impossibili e cast impliciti. Quel file da solo rappresentava il rischio maggiore dell'intera codebase, e l'ho saputo in 15 minuti di scansione automatica, non in ore di lettura manuale.

composer audit è il secondo check non negoziabile. Se le dipendenze hanno CVE note, lo saprai subito. Sul progetto bresciano, audit ha rivelato 3 vulnerabilità: una in guzzlehttp/psr7 (già patchata nella minor successiva), una in laravel/framework stesso (risolta aggiornando da 9.52 a 9.52.20), e una in un pacchetto custom installato da un repository GitHub privato che non esisteva più. Quest'ultimo era il segnale di un problema più profondo che ho approfondito nella fase 2.

Fase 2 (ore 4-8): mappatura dell'architettura

Questa fase risponde alla domanda "come è fatto questo progetto?" senza leggere il codice riga per riga. I punti chiave sono quattro:

Rotte e middleware. php artisan route:list --columns=method,uri,name,middleware ti dà la mappa completa delle porte d'ingresso dell'applicazione. Sul gestionale bresciano, il comando ha restituito 247 rotte. Di queste, 31 non avevano middleware di autenticazione - incluse 4 rotte API che restituivano dati di produzione. Non erano endpoint pubblici intenzionali: erano rotte dimenticate da un refactoring precedente.

Service provider. Il file config/app.php e bootstrap/providers.php (su Laravel 11+) elencano i service provider registrati. Se trovi provider custom che non corrispondono a pacchetti noti, hai trovato logica custom che va compresa. Sul progetto bresciano, c'erano 3 provider custom: uno per l'integrazione con il software di magazzino (WMS), uno per la fatturazione elettronica (SDI), uno per un sistema di notifiche interno. Questi tre provider erano le tre aree di business logic più dense e rischiose del progetto.

Schema del database. L'export dello schema senza dati è il modo più rapido per capire il dominio di business:

# Schema completo senza dati
mysqldump -u root -p --no-data gestionale_db > schema.sql

# Conteggio righe per tabella (ordine decrescente)
mysql -e "SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = 'gestionale_db'
ORDER BY table_rows DESC LIMIT 20;"

# Migration status (cosa è stato applicato, cosa è pendente)
php artisan migrate:status

Sul progetto bresciano, lo schema conteneva 67 tabelle. Di queste, 12 avevano nomi come temp_import_2023, orders_backup, users_old - tabelle create durante operazioni manuali e mai rimosse. La tabella orders aveva 3,2 milioni di record; la tabella order_items ne aveva 11,8 milioni. migrate:status mostrava 4 migration pendenti - mai eseguite in produzione perché "causavano problemi." Quelle migration pendenti contenevano fix a foreign key mancanti. La ragione per cui non erano state eseguite: il precedente sviluppatore aveva paura che bloccassero la tabella in produzione (paura legittima su tabelle da milioni di righe senza Online DDL).

Scheduler e code. Due aree invisibili dall'esterno ma spesso critiche per il business. php artisan schedule:list mostra tutti i task schedulati - e sul progetto bresciano ne ha rivelati 14, di cui 3 commentati nel kernel (disabilitati ma ancora presenti), 2 che scrivevano su file di log dedicati che nessuno leggeva, e 1 che chiamava un endpoint API esterno ogni 5 minuti per sincronizzare l'inventario con il WMS. Quest'ultimo era il cuore del flusso di magazzino: se smetteva di funzionare, gli ordini venivano confermati su prodotti già esauriti. Per le code: php artisan queue:work --once --verbose in ambiente di staging mostra cosa c'è in coda e cosa succede quando un job viene processato. Su Laravel 9+ con Horizon, la dashboard /horizon dà una visione completa dei worker, delle code, dei job falliti e dei tempi medi. Sul gestionale bresciano, Horizon non era installato - le code giravano con un singolo queue:work in un service systemd, senza supervisione, senza retry policy, senza dashboard. Se il worker moriva, i job si accumulavano silenziosamente fino al restart manuale.

Dipendenze non standard. composer show --tree mostra l'albero delle dipendenze. Se trovi pacchetti installati da repository VCS (non da Packagist), segnali rossi. Sul progetto bresciano, il pacchetto per la fatturazione elettronica era installato da un repository GitHub privato dell'ex sviluppatore - repository a cui il cliente non aveva accesso. Se lo sviluppatore avesse cancellato il suo account GitHub, il prossimo composer install sarebbe fallito e il deploy sarebbe stato impossibile. Questo era il secondo rischio critico dell'assessment, dopo il file di fatturazione con 342 errori PHPStan.

Stai cercando un Consulente Informatico esperto per prendere in carico un progetto Laravel orfano? Nel mio profilo professionale trovi l'esperienza concreta su subentri, code assessment e stabilizzazione di applicazioni senza documentazione.

Fase 3 (ore 8-16): tracciamento del flusso di business

Questa è la fase che nessuno strumento automatico può fare al tuo posto. La domanda è: cosa fa questa applicazione che genera fatturato? Segui il denaro.

Su un gestionale come quello bresciano, il flusso critico è: ordine → produzione → spedizione → fatturazione. Traccio ogni passaggio nel codice partendo dalle rotte (/orders/create, /orders/{id}/produce, /orders/{id}/ship, /invoices/generate) e seguendo i controller, i service, i model, le observer, i listener, i job in coda. Ad ogni passaggio annoto: cosa succede se questo pezzo fallisce? Chi se ne accorge? Quanto ci vuole a ripristinare? Queste annotazioni diventano la mappa di rischio.

Sul progetto bresciano, il flusso critico era: ordine → verifica disponibilità (WMS) → conferma → produzione → spedizione → fatturazione (SDI). Ho scoperto che il passaggio ordine → verifica disponibilità non usava una transazione database: se il WMS rispondeva lentamente, era possibile che due ordini confermassero lo stesso stock in parallelo. Il rischio era basso (capitava forse una volta al mese) ma il danno per caso era alto (ordine confermato, cliente avvisato, prodotto non disponibile, reputazione persa). Questo tipo di race condition è invisibile al code review superficiale - serve seguire il flusso end-to-end per vederlo.

Ma la scoperta più importante della fase 3 è stata che la logica di calcolo del prezzo finale - sconti, listini personalizzati, soglie volume - non stava nei service o nei model. Stava in un Blade template. Dentro resources/views/orders/create.blade.php, 180 righe di JavaScript calcolavano il prezzo totale lato client, e il controller salvava il risultato senza ricalcolarlo lato server. Un utente con le DevTools aperte poteva modificare il prezzo finale di qualsiasi ordine. Questo bug esisteva da almeno tre anni - nessuno l'aveva mai trovato perché nessuno aveva mai fatto un assessment del flusso end-to-end.

Per chi gestisce la fase successiva - i 90 giorni post-subentro in cui il debito tecnico viene misurato, prioritizzato e ripagato - i risultati della fase 3 sono il punto di partenza: ti dicono dove il rischio è immediato (business logic nel frontend), dove è latente (dipendenze da repository inaccessibili) e dove è gestibile (migration pendenti, tabelle orfane).

Fase 4 (ore 16-24): deliverable al titolare

Le ultime otto ore servono a tradurre tutto quello che hai trovato in un documento che un non-tecnico possa capire e su cui possa prendere decisioni. Il formato che uso è una singola pagina con tre sezioni - il modello è ispirato a come gli audit di qualità del codice vengono strutturati nel 2025-2026 con focus su impatto business anziché su metriche tecniche:

  • Da fixare questa settimana (rischio immediato per il business o la sicurezza): business logic nel frontend, rotte API senza autenticazione, dipendenza da repository privato inaccessibile.
  • Da fixare questo trimestre (rischio accumulato che peggiora nel tempo): 342 errori PHPStan nel modulo fatturazione, migration pendenti, tabelle orfane, mancanza di test.
  • Non preoccuparti di questo (debito tecnico che non impatta il business a breve): naming convention inconsistente, codice commentato, import non usati, mancanza di type hint.

Sul progetto bresciano, il deliverable conteneva 4 item nella prima sezione, 11 nella seconda, e 23 nella terza. La differenza tra un assessment utile e un report che finisce in un cassetto è la capacità di collegare ogni finding tecnico a una conseguenza di business che il titolare capisce senza traduzione. "342 errori PHPStan nel modulo fatturazione" non dice nulla a un imprenditore. "Il modulo che genera le fatture elettroniche ha 342 punti in cui il codice potrebbe fallire silenziosamente, e se una fattura esce con un importo sbagliato vi arriva un accertamento dall'Agenzia delle Entrate" - quello sì che lo capisce. Ogni item del report va formulato così: cosa c'è che non va, cosa succede se non lo fissi, e quanto costa fissarlo (in giorni di lavoro, non in euro - il costo giornaliero lo decide il titolare, non il consulente).

Il titolare ha guardato la prima sezione, ha capito immediatamente la gravità della business logic nel frontend, e ha autorizzato l'intervento il giorno stesso. Il fix del calcolo del prezzo - spostare la logica nel backend con validazione lato server - ha richiesto 6 ore di lavoro. Il rischio finanziario che eliminava era potenzialmente illimitato.

Dopo i primi fix critici, il percorso naturale è il subentro strutturato su Laravel con Horizon, scheduler e tutto il contorno che il predecessore ha lasciato in stato incerto - la stabilizzazione completa dell'applicazione come prerequisito per qualunque evoluzione futura.

Se hai ereditato un progetto Laravel senza documentazione e il tuo team non sa da dove cominciare, il primo passo non è scrivere codice - è capire cosa c'è. Un assessment strutturato di 48 ore costa meno di una settimana di sviluppo alla cieca e ti dà una mappa che orienta ogni decisione successiva: cosa toccare per primo, cosa evitare, e dove investire. Contattami se hai bisogno di un assessment: in due giornate di lavoro ti consegno la mappa di rischio del progetto con priorità ordinate per impatto di business, i tre interventi da fare subito e una roadmap trimestrale per il debito tecnico che hai ereditato.

Ultima modifica: