← Blog
Note di lavoro

Dal PHP legacy alla parità verificabile: un caso DFD di migrazione

Un gestionale PHP in uso, una conversione Laravel che deve raggiungere la parità prima del passaggio, e DFD (DoD-First Driven) guidato da un agente diverso: Codex. La tesi della serie regge, ma il segnale più grosso torna dallo stesso posto del terzo articolo: un gate che diceva 'fatto' senza esserlo.

Una matrice di parità tra un gestionale PHP legacy e la sua conversione Laravel, con celle 'equivalente', 'diverso', 'da collaudare', e un gate verde incrinato che non corrisponde allo stato reale del lavoro.

Nel primo articolo di questa serie ho sostenuto che con gli agenti il costo dello sviluppo non sparisce: migra a monte (specificare) e a valle (verificare). Nel terzo ho scoperto, provandoci, che il segnale più importante non veniva dai test ma da un gate che mentiva: una CI che non poteva fallire.

Questo è un quarto banco di prova, su un terreno diverso: non una feature nuova, ma una migrazione. Un gestionale legacy in PHP, ancora in uso, e una conversione Laravel che deve raggiungere la parità funzionale prima del passaggio. E un dettaglio che cambia il peso del resto: il lavoro tecnico non l’ha guidato Claude Code come negli esperimenti precedenti, ma Codex, con DFD — DoD-First Driven, un framework che sto sviluppando — e la sua CLI.

Attenzione: anche questo è un esperimento, non una dimostrazione. Un solo progetto, materiale reale e imperfetto, e alcune verifiche che non ho potuto rilanciare per limiti della sessione. Quello che segue è un caso onesto, con i suoi buchi dichiarati.

Perché una migrazione è il caso perfetto per la tesi

Una feature nuova ti lascia margine: se la spec è vaga, il comportamento “giusto” è in parte una scelta. Una migrazione no. Il comportamento giusto esiste già, è quello del sistema in uso, e il lavoro non è inventare: è dimostrare equivalenza. È il caso in cui il costo si sposta tutto su specificare cosa deve restare uguale e verificare che lo sia — esattamente la tesi del primo articolo, ma senza vie di fuga.

Il problema è che le regole del vecchio gestionale erano quasi tutte implicite: vivevano nel codice PHP e nelle abitudini di chi lo usa, non in un documento. DFD è servito a questo: trasformarle in decisioni esplicite e verificabili.

Regola implicita nel legacy Diventata, con DFD
Ruoli e permessi Decisione esplicita, con casi di test
Tariffe annuali Spec + verifica sugli importi storici
Cena separata dalla quota Regola a sé, collaudabile
Numeri assegnati alla consegna Decisione su quando e come si assegna
Anzianità Criterio esplicito e testato
Conservazione cartacea delle firme Vincolo di processo, dichiarato

Il perno di tutto è stata la matrice di parità: una tabella che, voce per voce, dice se il nuovo sistema è equivalente, diverso o ancora da collaudare rispetto al vecchio. È la versione da migrazione della DoD del primo articolo: non “cosa deve fare la feature”, ma “cosa deve restare identico, e come lo provo”.

Cosa ha funzionato

Qui la tesi regge, e si vede.

  • La parità applicativa è stata coperta da prove automatiche. Non “sembra uguale”: un test che fallisce se smette di esserlo.
  • Database nuovo separato dal legacy. Nessuna scrittura sul sistema in uso: la rete di sicurezza più banale e più spesso saltata.
  • L’importazione ha preservato gli importi storici e ha evitato di fondere automaticamente record ambigui — la tentazione peggiore di ogni import, quella che “pulisce” i dati facendoli sparire.
  • Password dei nuovi account con hash bcrypt. Auth e migrazioni dati sono le zone che nel primo articolo mettevo sotto revisione obbligatoria: qui sono state trattate come tali.
  • Niente CI: il progetto era in modalità solo sviluppo, una fase che DFD prevede esplicitamente — CI ancora prematura, ma verifiche locali tracciate come evidenza.

Questo ultimo punto, però, è anche la crepa.

Dove il materiale reale ha spinto indietro

Gli ostacoli più istruttivi non sono venuti dall’agente: sono venuti dai dati veri. È lo stesso fenomeno che nel primo articolo chiamavo “i requisiti emergono usando il prodotto”, qui in versione archeologica.

  • Il dump non offriva un listino annuale affidabile: le tariffe storiche andavano ricostruite.
  • Conteneva numeri duplicati tra iscritti.
  • Aveva uno stato “online” che il vecchio codice nemmeno interpretava.
  • Mancava un dataset di fixture iniziale: niente base nota su cui far girare le prove.

E sul processo, tre attriti concreti: gate DFD non sempre allineati alle attività reali (ci torno sotto), l’accesso SSH a Git non funzionava da quella sessione, e il runtime locale non permetteva di rilanciare i test.

La risposta giusta a dati così non è forzarli a essere puliti. È conservare e tracciare:

Problema nei dati Correttivo
Nessun listino annuale affidabile Tariffe storiche inattive ricostruite dagli importi effettivi delle iscrizioni
Numeri duplicati Numeri conflittuali conservati come provenienza, senza riassegnarli come attivi
Stato “online” non interpretato Stato mantenuto, non reinterpretato
Utenti amministrativi legacy Esclusi dall’import

È la stessa lezione del primo articolo vista da un altro lato: lì il rischio era il codice che nessuno ha chiesto, qui è la reinterpretazione che nessuno ha chiesto. In una migrazione, inventare un significato è un bug che si scopre mesi dopo.

Per il trasferimento su GitHub ho spostato il delta con un branch e una PR dedicati, controllando i file prima del merge ed escludendo dump e artefatti locali — nessun travaso cieco.

Il segnale, di nuovo: un gate che diceva “fatto”

E qui succede la cosa che non mi aspettavo, perché l’avevo già vista nel terzo articolo con un altro strumento e un altro agente.

Gli aggiornamenti dello stato DFD non erano sempre allineati al lavoro reale. Risultato: gate obsoleti. Celle della matrice che dicevano “collaudato” mentre il runtime non permetteva nemmeno di rilanciare i test; voci “pronte” basate su una PR unita e non su un collaudo osservato.

È lo stesso identico fallimento della CI che non poteva fallire, spostato di un livello:

Terzo articolo Questo caso
CI verde per via di cmd | tee file Gate DFD verde per via di uno stato non aggiornato
Il sigillo non provava ciò che dichiarava La matrice non provava la parità che dichiarava
Il segnale vero era nella pipeline, non nei test Il segnale vero era nello scarto tra gate e stato reale

La lezione non riguarda DFD più di quanto riguardasse GitHub Actions. Anzi: DFD gli strumenti per evitarlo ce li ha — distingue la modalità solo sviluppo dalla preparazione al rilascio, e una PR unita nella prima non autorizza a dire “pronto”. Il buco non era nel framework, ma nella disciplina di tenerne aggiornato lo stato. Il che riporta al punto vero: di cosa è evidenza un gate verde. Una PR unita dimostra che del codice è stato integrato. Non dimostra, da sola, un collaudo operativo, un ripristino riuscito o un rilascio pronto. Se il gate pretende di dirlo, deve essere agganciato a un fatto osservato — un test rilanciato, un ripristino eseguito — non allo stato che qualcuno ha dimenticato di aggiornare.

Il collegamento con la validazione in produzione è diretto: lo shift-right funziona perché i segnali di produzione sono fatti, non dichiarazioni. Un gate scollegato dallo stato reale è il contrario esatto di un canary.

Cosa mi porto a casa

DFD aiuta quando registra decisioni, evidenze e incertezze insieme al codice. È la forma concreta dell’“artefatto vivo” del primo articolo: la matrice di parità ha reso possibile seguire cosa fosse equivalente, diverso o ancora da collaudare, e le incertezze sono rimaste visibili invece di essere risolte di nascosto. Per una migrazione, dove il comportamento giusto esiste già, è esattamente lo strumento che sposta il costo dove deve stare.

Ma uno strumento di gate vale quanto la fedeltà dei suoi gate allo stato reale. E qui la tesi della serie prende la sua piega più scomoda: non basta avere i gate giusti, devono dire la verità nel momento in cui li guardi. Un verde che non corrisponde al lavoro fatto non è neutro — è peggio di nessun gate, perché autorizza a fidarsi.

Due esperimenti, due strumenti, due agenti diversi, stessa scoperta. Comincio a pensare che non sia un caso: quando il costo si sposta sulla verifica, il punto più fragile diventa il meccanismo che dichiara “verificato”.

Poscritto. Nel caso successivo, di nuovo con Claude Code e su due feature di Taita portate in produzione, il gate diceva la verità, ma solo sulla macchina dove avevo fatto le prove: i log delle evidenze non finivano nel repository. Il caso è qui: Da un feedback esterno al rilascio.

Limiti da dichiarare

  • Un solo progetto reale, con dati imperfetti; niente repliche.
  • Verifiche non rilanciabili nella sessione (runtime locale e accesso SSH): parte delle evidenze è quella raccolta, non riprodotta a comando.
  • La ricostruzione delle tariffe storiche dagli importi effettivi è una stima, non un dato di origine.
  • L’esperimento è stato guidato con Codex e DFD: non ho un confronto a parità di condizioni con un altro agente o un altro framework sullo stesso progetto.
#AI#agenti#migrazione#spec#legacy#Codex