Scritto originalmente in francese. Tradotto dall'IA — il significato è stato preservato, non la prosa.
Questo articolo prosegue ciò che ho scritto nel mio libro Le Contexte. Per leggerlo, scaricalo o acquistalo su Amazon. L’edizione Amazon è disponibile solo in francese; se desideri una versione in un’altra lingua, contattami.
Il TDD usa i test per guidare lo sviluppo. E se usassimo anche un'ontologia per preservare la coerenza di business di un software? È la strada che sto esplorando con Claude Code: partire dal codice per costruire un'ontologia, poi invertire progressivamente il percorso perché sia l'ontologia a guidare, controllare e accompagnare le evoluzioni del software.
Il codice funziona. Ma fa ancora il business giusto?
Con agenti IA come Claude Code siamo entrati in una fase in cui scrivere codice è sempre meno difficile.
Descrivo una funzionalità. L'agente esplora il codice esistente, modifica diverse classi, aggiunge funzioni, adatta interfacce, scrive test. Pochi minuti dopo mi presenta il suo lavoro.
Il codice compila. I test passano.
Ma resta una domanda: questo nuovo codice rispetta ancora la logica di business della mia applicazione?
Un agente può produrre un'implementazione tecnicamente corretta e introdurre un'incoerenza nel modello di dominio. Può usare due nomi per indicare lo stesso concetto, reintrodurre una nozione abbandonata o creare una regola che contraddice quelle già esistenti.
Il problema diventa davvero interessante quando l'applicazione cresce e gli agenti IA si fanno carico di una quota crescente delle modifiche.
Come dare loro un riferimento di business comune? Come rilevare una deriva? E come evitare che questo riferimento diventi l'ennesima documentazione, che prima o poi smette di corrispondere al software?
È partendo da queste domande che ho iniziato a sperimentare, nei miei progetti personali con Claude Code, un approccio che qui chiamo Ontology-Driven Design (ODD).
Per illustrarlo prenderò l'esempio di un ERP immaginario.
Primo passo: estrarre l'ontologia dal codice
Il mio punto di partenza era piuttosto semplice.
Considero il codice sorgente la fonte di verità del comportamento effettivamente implementato. Una specifica descrive un'intenzione; il codice rivela ciò che l'applicazione fa davvero.
Ho quindi chiesto a Claude Code di esplorare il codice della mia applicazione e di estrarne una rappresentazione del dominio di business in un file Markdown: ontology.md.
Questo file descrive in particolare:
- i concetti di business: Cliente, Ordine, Preventivo, Fattura, Nota di credito… ;
- le loro proprietà e relazioni;
- le regole di business e gli invarianti;
- gli stati e le loro transizioni;
- il vocabolario usato per indicare questi concetti.
Nel nostro ERP immaginario, un ordine cliente è associato a un cliente. Ha una o più righe. Può derivare da un preventivo. Può essere convalidato, annullato o fatturato.
Queste informazioni non sono per forza descritte nello stesso punto del codice. Alcune regole sono esplicite, altre sono sparse in diverse funzioni.
Claude Code può cercarle e provare a ricostruirle.
Il risultato non è una specifica perfetta. È un'interpretazione strutturata del business a partire dalla sua implementazione, con i limiti che ne derivano: una regola implicita può sfuggirgli, e un comportamento storico può essere scambiato per una regola intenzionale.
Ma ottengo qualcosa di interessante: una rappresentazione del dominio indipendente dai dettagli tecnici dell'implementazione.
Il file ontology.md diventa un punto di riferimento per capire il software.
Secondo passo: verificare il nuovo codice rispetto all'ontologia
Una volta disponibile l'ontologia, ho chiesto a Claude Code di usarla quando sviluppava nuove funzionalità.
L'idea era confrontare le modifiche fatte con i concetti e le regole di ontology.md.
Immagina per esempio che l'ontologia indichi che una fattura convalidata non può più essere modificata.
Se Claude Code aggiunge una funzionalità che permette di cambiarne le righe dopo la convalida, deve segnalare questa contraddizione.
Allo stesso modo, se l'ontologia distingue chiaramente un preventivo da un ordine, l'agente non deve introdurre una funzione che mescola le responsabilità dei due concetti senza una ragione esplicita.
È una prima rete di sicurezza.
Ma ha un difetto.
La verifica arriva dopo la produzione del codice.
L'agente ha già fatto il suo lavoro. Magari ha creato diverse classi, aggiunto metodi e costruito test intorno a un'interpretazione sbagliata del business.
Possiamo rilevare il problema, ma poi bisogna tornare sul codice prodotto.
In altre parole, avevo aggiunto un controllo a posteriori. Utile, ma insufficiente.
Terzo passo: ispirarsi al Test-Driven Development
È qui che ho pensato al TDD.
Il principio del Test-Driven Development è noto: prima di implementare un comportamento, si scrive un test che esprime ciò che ci si aspetta. Il test fallisce, si scrive il codice necessario per farlo passare, poi si migliora l'implementazione mantenendo questa rete di sicurezza.
Perché non applicare una logica simile all'ontologia?
Ho quindi chiesto a Claude Code di cambiare il suo processo.
Prima di scrivere codice, deve confrontare la funzionalità richiesta con l'ontologia esistente.
L'ordine delle operazioni cambia.
Prendiamo una nuova richiesta:
«Voglio permettere la creazione di un ordine cliente senza passare obbligatoriamente da un preventivo.»
Se l'ontologia indica che un ordine deve per forza nascere da un preventivo, l'agente rileva una contraddizione prima di toccare il codice.
Ma questa contraddizione non è per forza un errore nella richiesta.
Può corrispondere a un'evoluzione del tutto legittima del business.
L'agente deve quindi riconoscere che l'intenzione dello sviluppatore modifica una regola esistente, proporne l'evoluzione e determinare le conseguenze sui concetti collegati.
Potrà poi sviluppare la funzionalità tenendo conto del modello di dominio rivisto.
Il processo diventa:
- Capire la funzionalità richiesta.
- Individuare i concetti e le regole dell'ontologia coinvolti.
- Segnalare le eventuali contraddizioni tra la richiesta e il modello esistente.
- Risolvere queste contraddizioni, distinguendo evoluzione voluta ed errore di progettazione.
- Sviluppare la funzionalità.
- Verificare che il codice prodotto sia coerente con il modello di dominio atteso.
Non è TDD in senso stretto: un'ontologia non è una suite di test automatizzati, e un'analisi fatta da un LLM non offre le stesse garanzie di un'asserzione deterministica.
Ma la filosofia è vicina: definire aspettative e vincoli prima di implementare, invece di scoprire le incoerenze a cose fatte.
A questo punto l'ontologia inizia ad avere un ruolo attivo nello sviluppo.
E questo fa emergere un nuovo problema.
Quarto passo: un'ontologia che deve evolvere con il codice
Se l'ontologia diventa un riferimento per lo sviluppo, come mantenerla aggiornata?
Non voglio assolutamente creare un nuovo documento che bisognerà ricordarsi di modificare a mano a ogni evoluzione dell'applicazione.
Sarebbe tornare al problema di partenza: una documentazione che si disallinea dal codice.
L'idea successiva è quindi introdurre una parte meccanica nel processo.
Uno script analizza i cambiamenti del codice sorgente, in particolare a partire da Git, e individua gli elementi aggiunti, modificati o eliminati:
- funzioni e metodi;
- classi e interfacce;
- moduli e package;
- firme e contratti di API;
- strumenti, risorse e contratti esposti tramite MCP;
- relazioni e comportamenti di business riconoscibili nelle modifiche.
L'obiettivo non è ricostruire l'ontologia a partire dai dettagli tecnici dell'applicazione.
Al contrario, voglio restare indipendente dalle scelte di implementazione. Un'architettura esagonale, il Domain-Driven Design o qualsiasi altra organizzazione del codice (anche molto sporca) non devono imporre la loro struttura all'ontologia.
L'ontologia rappresenta il dominio di business, non l'architettura tecnica.
Una classe non è per forza un concetto di business. Un nuovo package può essere solo una riorganizzazione tecnica. Una funzione può implementare una regola di business senza costituire di per sé un nuovo concetto.
Bisogna quindi separare due operazioni.
La prima è meccanica: stabilire con precisione cosa è cambiato.
La seconda è semantica: capire se questi cambiamenti introducono, modificano o contraddicono dei concetti di business.
È qui che entra in gioco l'LLM.
Lo script produce un inventario delle modifiche. L'LLM confronta questo inventario con l'ontologia attuale e con le direttive che hanno avviato lo sviluppo.
A quel punto possono presentarsi diverse situazioni.
Compare un nuovo concetto di business. L'agente può proporne o prepararne l'aggiunta all'ontologia, con le sue relazioni e le regole associate.
Un concetto esistente cambia significato. Bisogna stabilire se si tratta di un'evoluzione voluta o di una deriva.
Due concetti diventano incoerenti. L'agente segnala la contraddizione, con i riferimenti al codice coinvolto.
Il vocabolario diverge. Due nomi indicano la stessa realtà di business, oppure un nome esistente viene usato per un'altra nozione.
Il cambiamento è puramente tecnico. Nessuna modifica all'ontologia è necessaria.
Un punto resta essenziale: il codice non deve avere automaticamente ragione.
Se un agente introduce un errore, non voglio che un secondo agente modifichi l'ontologia per rendere quell'errore accettabile.
Il triangolo: intenzione, codice e ontologia
È qui, credo, che sta la vera evoluzione del concetto.
Confrontare il codice e l'ontologia non basta.
Bisogna mettere a confronto tre elementi:
L'intenzione: ciò che lo sviluppatore ha chiesto a Claude Code.
Il codice: ciò che è stato effettivamente implementato.
L'ontologia: i concetti e le regole di business di riferimento.
È il confronto tra questi tre elementi che permette di capire la natura di uno scostamento.
Torniamo al nostro ERP immaginario.
L'ontologia indica che un ordine cliente nasce obbligatoriamente da un preventivo.
Lo sviluppatore chiede esplicitamente di rendere il preventivo facoltativo.
Claude Code modifica le funzioni interessate e introduce la possibilità di creare direttamente un ordine.
Ora l'ontologia e il nuovo codice sono in contraddizione. Ma il codice rispetta l'intenzione.
Il risultato corretto non è quindi rifiutare sistematicamente la modifica. È proporre l'evoluzione corrispondente dell'ontologia, poi cercare gli altri punti del software che presuppongono ancora che il preventivo sia obbligatorio.
Se invece Claude Code avesse eliminato questo obbligo senza che nessuna direttiva lo chiedesse, lo scostamento sarebbe sospetto.
La differenza è fondamentale.
Un'incoerenza non è sempre un bug. Può segnalare una trasformazione voluta del business.
Il compito del sistema non è impedire il cambiamento, ma renderlo esplicito, coerente e tracciabile.
Verso un vero ciclo di sviluppo guidato dall'ontologia
Ora immagino un processo che interviene in tre momenti.
Prima dello sviluppo: capire il business
Quando riceve una nuova funzionalità, Claude Code individua i concetti dell'ontologia coinvolti.
Non ha bisogno di caricare tutta l'ontologia. Può selezionare i concetti, le relazioni e le regole utili alla richiesta.
Poi verifica che la funzionalità sia compatibile con questo modello, o segnala le evoluzioni necessarie.
Lo sviluppo parte così da una comprensione esplicita del dominio.
Durante e dopo lo sviluppo: rilevare le derive
Un analizzatore meccanico individua le modifiche del codice sorgente.
Strumenti come Tree-sitter permettono in particolare di analizzarne la struttura sintattica e di identificare gli elementi modificati (è il mio prossimo cantiere). Anche le evoluzioni dei contratti di API e delle interfacce MCP dicono qualcosa sulle capacità esposte dall'applicazione.
L'LLM si basa su questi elementi per cercare le incoerenze con l'ontologia.
Questa operazione potrebbe essere integrata nel workflow di Claude Code tramite hook, per esempio dopo una modifica o alla fine di un task.
Non si tratta di chiedere a un LLM di rileggere alla cieca tutta l'applicazione a ogni cambiamento, ma di concentrare l'analisi sugli elementi toccati.
Prima dell'integrazione: controllare la coerenza
Un'ultima verifica potrebbe avvenire prima del merge del codice.
Produrrebbe un report che indica:
- i concetti nuovi o modificati;
- le regole di business potenzialmente toccate;
- le incoerenze rilevate;
- le modifiche all'ontologia proposte;
- le decisioni che richiedono una convalida umana.
L'obiettivo è ottenere un controllo riproducibile, abbastanza preciso da essere utile, senza trasformare ogni modifica del codice in una revisione interminabile del modello di dominio.
Mantenere il legame tra i concetti e la loro implementazione
Un miglioramento complementare consisterebbe nel mantenere una tracciabilità tra l'ontologia e il codice.
Un concetto come Invoice potrebbe essere associato a diverse classi, funzioni, eventi o operazioni di API che contribuiscono alla sua implementazione.
Queste associazioni non definiscono il concetto. Indicano dove è implementato.
Questa distinzione permetterebbe di rispondere a domande interessanti.
Se modifico il concetto di fattura, quali parti del codice rischiano di essere toccate?
Se una funzione di business scompare, quale regola dell'ontologia potrebbe non essere più implementata?
Se compare una nuova operazione di API, espone un concetto esistente o rivela una nozione nuova?
E se una classe cambia nome, si tratta di un semplice refactoring o di una modifica reale del dominio?
Questa mappa potrebbe essere aggiornata progressivamente, man mano che il codice evolve.
Dovrebbe però conservare le prove di ogni associazione: file, simboli e cambiamenti osservati. Una relazione dedotta dall'LLM non va confusa con una corrispondenza dimostrata.
Dagli invarianti dell'ontologia ai test automatizzati
Un'altra strada consiste nel rendere eseguibile una parte dell'ontologia.
Alcune regole di business possono essere espresse come invarianti.
Per esempio:
- una fattura convalidata non può più essere modificata;
- una fattura deve avere almeno una riga;
- una nota di credito fa riferimento alla fattura che corregge;
- un ordine annullato non può essere spedito.
Alcune di queste regole potrebbero servire a produrre test automatizzati, o a verificare che i test esistenti le coprano.
Si otterrebbero così due livelli complementari.
Il primo sarebbe semantico: l'LLM esamina la coerenza tra le intenzioni, i concetti e il codice.
Il secondo sarebbe deterministico: test e vincoli eseguibili verificano i comportamenti che è possibile formalizzare.
L'ontologia non sostituirebbe quindi né i test unitari né i test di integrazione.
Potrebbe aiutare a spiegare perché certi comportamenti vanno testati, e a individuare regole di business ancora prive di verifica.
Un'ontologia viva, ma non un'ontologia opportunista
Questo approccio comporta un rischio importante.
Se l'ontologia cambia sistematicamente per inseguire il codice, smette di essere una rete di sicurezza. Diventa una semplice documentazione rigenerata di continuo.
Al contrario, se vieta qualsiasi evoluzione che contraddica le sue regole attuali, impedisce al prodotto di cambiare.
Bisogna quindi gestire esplicitamente le evoluzioni dell'ontologia.
Distinguo tre situazioni.
L'evoluzione è intenzionale e chiara. Il sistema può preparare una modifica versionata dell'ontologia, o persino applicarla in un branch di lavoro prima della convalida.
La modifica sembra incoerente con l'intenzione. Il sistema propone una correzione del codice invece di un aggiornamento dell'ontologia.
L'intenzione è ambigua. Il sistema chiede una decisione invece di inventare una nuova regola di business.
L'ontologia diventa così un contratto evolutivo tra l'intenzione e l'implementazione.
Questo presuppone anche di conservare uno storico, per esempio con un piccolo registro delle decisioni ispirato agli ADR. Un concetto rinominato non deve diventare due concetti indipendenti. Una regola eliminata deve poter essere collegata alla decisione che ne ha giustificato la scomparsa.
L'evoluzione dell'ontologia deve essere tracciabile quanto quella del codice.
Misurare la coerenza invece di contare le righe di codice
Questo approccio potrebbe anche produrre alcuni indicatori interessanti.
La copertura dell'ontologia misurerebbe la quota dei concetti di business individuati nel codice che hanno una rappresentazione nell'ontologia.
La tracciabilità misurerebbe quanti concetti dispongono di riferimenti verificabili verso la loro implementazione.
Il numero di incoerenze confermate e non risolte darebbe un'idea della deriva tra il codice e il modello.
Infine, la quota di invarianti associati a test eseguibili potrebbe aiutare a misurare la solidità della rete di sicurezza.
Questi numeri non sarebbero indicatori di qualità assoluti. Il loro valore dipende interamente dalla capacità del sistema di distinguere i veri concetti di business dal rumore tecnico.
Ma potrebbero rendere osservabile qualcosa che oggi resta in gran parte implicito: la coerenza concettuale di un software nel corso delle sue evoluzioni.
E se il processo funzionasse nei due sensi?
All'inizio il mio procedimento era unidirezionale.
Il codice produceva l'ontologia.
Poi ho aggiunto un controllo inverso: l'ontologia serviva a verificare il nuovo codice.
Ispirandomi al TDD, ho introdotto un passo preliminare: confrontare le intenzioni con l'ontologia prima di iniziare a sviluppare.
La prossima evoluzione consiste nell'automatizzare il rilevamento e la qualificazione dei cambiamenti, per mantenere questa coerenza nel tempo.
Ma ora vedo una possibilità in più.
Il codice potrebbe far evolvere l'ontologia, e l'ontologia potrebbe guidare le evoluzioni del codice.
Se una modifica del codice fa emergere un nuovo concetto di business, il sistema propone di integrarlo nell'ontologia.
Se una richiesta implica l'evoluzione di un concetto esistente, l'ontologia permette di individuare le regole e le implementazioni potenzialmente coinvolte prima ancora di cominciare.
Il riferimento non è più solo prodotto dal software. Partecipa alla sua trasformazione.
È questo ciclo che mi interessa.
Un'idea vecchia, un uso da reinventare
Sarebbe sbagliato presentare l'Ontology-Driven Design come un'idea del tutto nuova.
L'uso delle ontologie per progettare applicazioni esiste da tempo. Già nel 2005 Pavel Hruby descriveva un approccio di Ontology-Based Domain-Driven Design.
Più di recente, lavori come ODDToolkit hanno esplorato la generazione di artefatti software a partire da ontologie formali.
La mia esplorazione è un po' diversa.
Non cerco per forza di generare il software da un'ontologia. E non voglio nemmeno imporre OWL, RDF o un altro formalismo per cominciare.
Cerco di usare un'ontologia di business come riferimento vivo di coerenza per agenti IA che modificano il codice sorgente.
La sfumatura è importante.
Il modello di dominio non sostituisce l'implementazione. Non pretende di descrivere tutta la complessità di un software. Non garantisce, da solo, l'assenza di bug.
Offre un quadro di riferimento esplicito per evitare che ogni agente, a ogni nuovo task, ricostruisca la propria interpretazione del business.
Ciò che resta da dimostrare
Sono ancora in una fase di sperimentazione personale.
Ho cominciato generando un'ontologia dal codice, poi chiedendo a Claude Code di controllare le sue modifiche a partire da essa. In seguito ho fatto evolvere le direttive per spostare questa verifica a monte dello sviluppo.
Il ciclo meccanico di sincronizzazione, la tracciabilità sistematica e i controlli automatizzati sono i prossimi filoni di lavoro.
Molte domande restano aperte.
L'analizzatore riuscirà a rilevare una vera modifica di business quando nessun nome di classe cambia? Quanti falsi positivi produrrà l'LLM? JEV potrà rispondere a questa esigenza? È un altro cantiere, da aprire un giorno. Come evitare di appesantire l'ontologia con dettagli tecnici? Con quale frequenza bisogna riesaminare le corrispondenze tra concetti e codice? E soprattutto, il guadagno in coerenza giustificherà il costo di esecuzione e di manutenzione dell'approccio?
Bisognerà mettere alla prova questo approccio su vere evoluzioni del software, con cambiamenti di business voluti, incoerenze introdotte artificialmente e refactoring puramente tecnici.
Non pretendo quindi di aver definito un nuovo metodo di sviluppo convalidato.
Descrivo una direzione che sto sperimentando.
Il business come rete di sicurezza
Le pratiche di sviluppo hanno moltiplicato via via le protezioni.
I test unitari proteggono comportamenti. I test di integrazione proteggono interazioni. Il controllo dei tipi e gli strumenti di analisi statica rilevano alcune categorie di errori. Le regole di architettura aiutano a preservare la struttura del software.
Ma quando gli agenti IA producono una quota crescente del codice, diventa importante un'altra domanda:
Chi protegge la coerenza del modello di dominio che questi agenti manipolano?
Il DDD offre da tempo una parte della risposta, con il linguaggio ubiquo, i modelli di dominio e i confini di contesto. L'architettura esagonale aiuta a isolare il business dalle preoccupazioni tecniche.
L'Ontology-Driven Design che sto esplorando cercherebbe di aggiungere a questi principi un meccanismo operativo: rendere esplicito il modello di dominio, confrontarlo con le intenzioni di sviluppo, poi controllarne di continuo la coerenza con il codice.
In fondo, il mio obiettivo non è produrre più documentazione.
È permettere a Claude Code di sviluppare più velocemente senza reinventare in silenzio il business a ogni funzionalità.
Il codice resta la verità del comportamento implementato. L'ontologia diventa il riferimento del significato di business. L'intenzione spiega perché quel significato può cambiare.
Ed è dal loro confronto che potrebbe nascere una nuova rete di sicurezza per lo sviluppo assistito dall'IA.