Ubiquitous Language come sistema di controllo per lo sviluppo assistito da LLM
I Large Language Model (LLM) possono scrivere codice, generare suggerimenti architetturali e persino proporre modelli di dominio. La loro fluidità linguistica spesso dà l’impressione che comprendano davvero il sistema che stanno aiutando a progettare.
Ma questa fluidità nasconde un limite fondamentale:
Il modello non comprende il tuo dominio, reagisce semplicemente al linguaggio che gli fornisci.
Per questo motivo, la disciplina più importante quando si collabora con un LLM non riguarda la scritura di prompt sofisticati o la conoscenza di tecniche particolari. È la stessa disciplina che sta al centro del Domain-Driven Design: l’Ubiquitous Language.
Quando utilizzi un LLM per generare codice, progettare API, definire eventi o proporre strutture architetturali, la qualità del risultato dipende quasi interamente dalla precisione e dalla coerenza del linguaggio che utilizzi.
In pratica, l’Ubiquitous Language diventa il sistema di controllo dello sviluppo assistito dall’AI.
Perché il linguaggio conta ancora di più con i modelli LLM
In un team di sviluppo tradizionale, un linguaggio ambiguo è scomodo ma gestibile: gli sviluppatori fanno domande di chiarimento, revisionano le decisioni e gli esperti di dominio correggono eventuali incomprensioni.
Gli esseri umani risolvono naturalmente l’ambiguità. Gli LLM no!
Quando il linguaggio è vago o incoerente, il modello non può chiedere chiarimenti. Al contrario, riempie i vuoti di conoscenza generando ciò che appare statisticamente plausibile. Il risultato può sembrare corretto, ma potrebbe non riflettere le vere regole del tuo dominio.
Questo significa che l’ambiguità ha una conseguenza diversa quando si collabora con un modello LLM: espande lo spazio delle possibili interpretazioni.
Per aiutarti a capire meglio il mio punto di vista, prendi questo esempio di semplice richiesta da fare a un agente LLM:
“Crea gli eventi del sistema.”
Il tuo collega in team ti porrebbe subito alcune domande: “Quale sistema?”, “Quale parte del sistema?”, “Quali convenzioni di naming useresti?”, “Quali confini del sistema?”.
Un LLM non farà queste domande, anzi genererà subito un output che sembra ragionevole.
Conserva il risultato appena restituito e confrontalo con quello che lo stesso LLM ti genera da un prompt scritto con un linguaggio di dominio disciplinato:
“Definisci i domain event per il Bounded Context Order nella piattaforma BrewUp. Usa nomi al passato e non introdurre nuovi Aggregate.”
Noterai sicuramente una differenza che non è stilistica, bensì strutturale.
Nel secondo caso, il modello ha un vocabolario chiaro e confini espliciti. Le possibili interpretazioni si riducono drasticamente e l’output generato diventa molto più allineato con il dominio previsto.
In altre parole:
La precisione del linguaggio riduce l’ambiguità nei risultati generati.
Ubiquitous Language come sistema di confini
Nel Domain-Driven Design, l’Ubiquitous Language assicura che ogni concetto importante abbia un significato condiviso all’interno del team.
Termini come Order, Payment o Inventory Reservation non sono parole generiche. Sono concetti di dominio precisi che esistono all’interno di confini ben definiti.
Quando collabori con un agente LLM, o agente AI, questi confini diventano ancora più importanti.
Se nei prompt dovessi alternare più termini per indicare lo stesso concetto (per esempio Order, Purchase, Request) il modello li interpreterebbe come segnali diversi e inizierebbe a generare strutture che non erano mai state previste.
Faccio un esempio prendendo il termine Order. Chiedendo all’agente AI di definire eventi per un Order, potresti ritrovarti come risultato eventi come PurchaseCreated o RequestSubmitted, semplicemente perché quei tre termini compaiono spesso in contesti simili.
Il modello non sta prendendo una decisione concettuale. Sta seguendo pattern linguistici.
Per questo motivo un Ubiquitous Language disciplinato diventa essenziale.
Se il concetto di dominio è Order, allora tutto deve riferirsi coerentemente a Order:
- OrderCreated
- OrderPaid
- OrderCancelled
La coerenza stabilizza il processo di generazione e previene la deriva concettuale.
Scrivere codice con un LLM richiede disciplina linguistica
Quando chiedi a un LLM di scrivere codice, la precisione del linguaggio diventa ancora più critica.
La generazione di codice dipenderà fortemente da come i concetti saranno descritti. Se il vocabolario del dominio che deciderai di utilizzare sarà poco chiaro, lo saranno anche le strutture generate.
Faccio un altro esempio. Considera il seguente prompt:
“Genera un servizio che gestisce gli acquisti.”
Il modello dovrà sicuramente indovinare il significato del termine “acquisto”, magari ponendosi domande quali: “È la creazione di un ordine?”, “Il processo di pagamento?”, “Il checkout?”.
Scrivo la stessa richiesta, utilizzando un linguaggio di dominio chiaro:
“Genera un servizio che gestisce il posizionamento degli Order nel bounded context Ordering. L’aggregate Order emette un evento OrderPlaced quando il pagamento viene confermato.”
Avrai notato che ho utilizzato un vocabolario per definire il sistema:
- Order, il concetto centrale
- OrderPlaced, l’evento
- SalesOrder, il confine del contesto
Poiché il linguaggio è strutturato, il codice generato tenderà a riflettere quella struttura.
L’agente AI non comprende il dominio ma, seguendo i segnali presenti nel linguaggio, genera il codice. Quando questi segnali sono precisi, il codice risultante si allinea con il modello di dominio.
Ubiquitous Language come sistema di vincoli
Lo abbiamo visto prima, quando il linguaggio di un prompt è vago, il modello LLM può prendere possibili direzioni per completare la richiesta. Quando invece il linguaggio è preciso, l’insieme delle possibili risposte si riduce ed è più prevedibile. Questo è particolarmente utile quando si generano:
- Domain Event
- Aggregate
- Command
- API
- Confini di servizi
Per rendere meglio il concetto, prendiamo due prompt di esempio: uno scritto con un prompt senza vincoli e l’altro utilizzando un linguaggio vincolato.
Prompt non vincolato:
“Crea i command del sistema.”
Prompt vincolato:
“Crea i command per l’aggregate Order nel bounded context SalesOrder. I command devono rappresentare intenzioni dell’utente e non devono bypassare gli invarianti dell’aggregate.”
Il secondo prompt incorpora direttamente la conoscenza del dominio nel linguaggio. L’output generato dal modello LLM tenderà quindi ad allinearsi a quella conoscenza, perché il vocabolario limita ciò che è plausibile.
Il modello LLM diventa più utile non perché sia più intelligente, ma perché il linguaggio guida il risultato.
Quando il modello LLM deraglia, controlla il linguaggio
Quando un modello produce un risultato errato, la reazione più comune è pensare che il modello abbia sbagliato.
Spesso però, il vero problema sta nel linguaggio utilizzato.
Se l’output prodotto dall’agente AI introduce concetti o confini inattesi, ciò spesso significa che il linguaggio del dominio utilizzato è generico, poco dettagliato.
Invece di chiedersi “Perché il modello ha sbagliato?” è meglio porsi questa domanda:
“Quali parti del linguaggio di dominio erano poco chiare?”
Queste sono alcune tra le cause più comuni di un linguaggio succinto:
- confini tra Bounded Context non definiti chiaramente;
- naming inconsistente degli aggregati;
- regole di dominio implicite ma mai esplicitate;
- mescolanza tra termini tecnici e termini di business.
Ogni volta che un prompt soffre di queste lacune, il modello LLM le colma applicando pattern appresi altrove.
In questo senso, gli LLM funzionano anche come uno specchio della chiarezza del tuo modello di dominio:
Se il linguaggio è preciso, l’output tende a rimanere coerente.
Se il linguaggio è confuso, l’output rivela quella confusione.
Il ruolo del software architect non scompare
Usare un modello LLM per generare codice o esplorare soluzioni architetturali non elimina la necessità della responsabilità architetturale.
Il modello può aiutare a:
- strutturare idee;
- esplorare alternative;
- generare codice o artefatti di dominio.
Lo stesso modello però non può verificare la bontà dei risultati in rapporto alle regole di business. Questo è compito dei software architect e degli esperti di dominio.
La responsabilità del software architect cambia: dalla produzione manuale di ogni artefatto alla progettazione di vincoli linguistici che guidano il processo di generazione.
Il lavoro evolve quindi dalla scrittura a mano all’ingegnerizzazione del linguaggio che guida la collaborazione.
L’intuizione chiave
Quando si lavora con i modelli LLM, il rapporto tra linguaggio e modellazione diventa più diretto che mai.
I modelli di dominio sono espressi attraverso il linguaggio.
Il comportamento dei modelli LLM è guidato dal linguaggio.
Un concetto che crea un allineamento molto potente, ma che allo stesso tempo introduce un rischio.
Se trattassimo i modelli LLM come collaborati guidati dal linguaggio e vincolati dall’Ubiquitous Language, diventerebbero degli assistenti estremamente utili per l’esplorazione e la generazione di codice.
La stessa disciplina che il Domain-Driven Design ha introdotto per la collaborazione tra esseri umani diventa altrettanto importante nella collaborazione tra umani e agenti AI.
Il principio fondamentale resta semplice:
Un linguaggio preciso produce sistemi precisi.
E quando un modello LLM entra nel processo di sviluppo, l’Ubiquitous Language diventa il timone che mantiene la rotta tra il sistema e il dominio.