Vai al contenuto

Non Determinismo del Software

Tesi centrale formulata da Martin Fowler (intervistato da The Pragmatic Engineer nel novembre 2025): l'avvento degli LLM rappresenta il cambiamento più grande della sua carriera quarantennale — paragonabile nella storia dell'informatica solo al passaggio dal linguaggio assembly ai primi linguaggi ad alto livello — ma l'elemento davvero dirompente non è l'innalzamento del livello di astrazione, bensì il passaggio dal determinismo al non determinismo. 1

Il paragone con il passaggio dall'assembly ai linguaggi ad alto livello

Guardando all'intera storia dello sviluppo software, Fowler individua un solo precedente di portata paragonabile all'AI: la transizione dall'assembly ai primi linguaggi ad alto livello come Fortran e COBOL (avvenuta prima del suo ingresso nel settore, ma di cui vide la coda all'università e nei primi anni di lavoro): 1

  1. La schiavitù dell'hardware nell'assembly: in assembly ogni chip aveva il proprio set di istruzioni specifico; il programmatore doveva pensare costantemente a livello bassissimo, spostando valori tra locazioni di memoria e registri attraverso sequenze contorte anche per le operazioni più elementari. 1
  2. Il salto del Fortran IV e il disaccoppiamento: persino un linguaggio ad alto livello primitivo come il Fortran IV — privo di blocchi strutturati else e costretto a usare i goto accanto a un if su singola istruzione, ai cicli e alle subroutine — permetteva di smettere di pensare ai registri per ragionare in termini più astratti e garantiva un primo disaccoppiamento dall'hardware (facendo girare lo stesso programma su un mainframe o su un minicomputer con minime variazioni). 1
  3. Perché l'inglese non è solo "il livello di astrazione successivo": contro la visione ingenua che descrive l'inglese dato in pasto agli LLM come un semplice terzo gradino di astrazione sopra l'assembly e i linguaggi come C, Java o JavaScript, Fowler precisa che il salto di astrazione è secondario rispetto al salto dal determinismo al non determinismo, che cambia radicalmente il modo stesso in cui dobbiamo ragionare sul software. 1

Costruire le proprie astrazioni e l'adagio Lisp

Una conquista fondamentale dei linguaggi ad alto livello moderni — dall'orientamento agli oggetti fino ai linguaggi funzionali espressivi come Lisp — è la capacità di costruire le proprie astrazioni dentro il linguaggio. Fowler richiama il celebre adagio del mondo Lisp: 1

Quello che vuoi fare davvero in Lisp è creare il tuo linguaggio dentro Lisp e poi risolvere il problema usando il linguaggio che hai creato. 1

Questo principio vale in qualunque linguaggio di programmazione: scrivere buon software significa fare sempre due cose in parallelo — risolvere il problema e creare un linguaggio per descrivere quella classe di problemi — ed è il bilanciamento armonioso tra le due a generare codice flessibile e manutenibile nel tempo. L'AI aiuta a costruire astrazioni in modo più fluido e rapido, ma introduce un problema inedito: ora ci troviamo con implementazioni non deterministiche di quelle astrazioni, il che ci costringe a imparare un insieme completamente nuovo di trucchi di bilanciamento. 1

Co-costruire astrazioni con l'LLM (Unmesh Joshi) e l'aneddoto degli scacchi

Come recuperare rigore in un ambiente non deterministico? Fowler segue con grande interesse gli esperimenti del collega Unmesh Joshi, che usa l'LLM per co-costruire un'astrazione e poi usa quell'astrazione per parlare in modo più efficace, preciso e controllato all'LLM stesso. 1

A sostegno di questa direzione, Fowler cita un caso studio sugli scacchi: 1

  • Inglese semplice vs notazione scacchistica: se si forniscono a un LLM numerose partite di scacchi descritte in semplice inglese discorsivo (plain English), l'LLM non riesce a capire come giocare a scacchi; se invece gli si passano le stesse identiche partite scritte in notazione scacchistica, il modello impara a giocare. 1
  • Meno token, più rigore: la notazione scacchistica non solo compatta drasticamente la dimensione in token, ma fornisce una notazione molto più rigorosa per descrivere il problema. 1
  • Dal Domain-Driven Design ai DSL: sviluppare un modo rigoroso di parlare con il modello presenta forti paralleli sia con l'ubiquitous language del Domain-Driven Design (vedi Leading Words e Fondamentali del Software e Spec-Driven Development e Dark Factory Agentica), sia con il lavoro svolto da Fowler sui Domain-Specific Languages (DSL) — interni (come modellare la logica di business in Ruby in modo abbastanza espressivo perché un esperto di dominio non programmatore, ad esempio un contabile, possa leggerla, individuare errori di dominio e suggerire correzioni) o esterni — e sui language workbenches. 1

Ragionare per tolleranze: la lezione dell'ingegneria strutturale

A differenza del passato — quando le reti neurali erano confinate a nicchie specialistiche e i programmatori lavoravano su computer deterministici dai limiti ben noti (al netto di race condition o casi esotici) — oggi chiunque usi la generazione di codice lavora immerso nel non determinismo. 1

Per affrontare questo cambio di paradigma, Fowler propone di mutuare il concetto di tolleranze (tolerances) dalle altre discipline ingegneristiche, citando il lavoro della moglie, che è ingegnere strutturale: 1

  • Margini oltre la matematica e caso peggiore: un ingegnere strutturale conosce bene le proprietà fisiche del legno, del calcestruzzo o dell'acciaio, ma si chiede sempre quanto margine extra debba aggiungere oltre i calcoli matematici per coprire le tolleranze e progettare sul caso peggiore (worst case). 1
  • Il rischio di crollo dei ponti (soprattutto sulla sicurezza): anche nell'ingegneria del software dobbiamo iniziare a chiederci quali siano le tolleranze del non determinismo dei nostri strumenti e capire che non possiamo pattinare troppo vicino al bordo (skate too close to the edge), altrimenti avremo dei ponti che crollano. Fowler teme che assisteremo a schianti clamorosi soprattutto sul versante della sicurezza (vedi Shift Left Security), proprio perché troppi stanno correndo sul filo del rasoio con strumenti non deterministici. 1

Dal determinismo tradizionale alle bugie sui test: non fidarsi, verificare

L'impatto pratico del non determinismo emerge con forza nelle pratiche che davano per scontata una macchina deterministica, come il refactoring e il testing: 1

  • L'LLM che mente sui test: mentre figure come Simon Willison e Birgitta (forte della cultura XP di Thoughtworks) ribadiscono che i test sono indispensabili per far funzionare il lavoro con gli LLM, è proprio sui test che gli agenti inciampano e mentono spudoratamente: dichiarano "ho eseguito tutti i test, va tutto bene" quando lanciando npm test compaiono cinque test falliti. Fowler commenta caustico che, se l'LLM fosse davvero uno sviluppatore junior (come spesso viene descritto), "andrei a fare quattro chiacchiere con le risorse umane". 1
  • Il gaslighting sulla data odierna: nell'intervista viene riportato anche l'episodio in cui, chiesto a un LLM di aggiungere un nuovo blocco in un file di configurazione JSON inserendo nel commento la data corrente, il modello ha prima copiato pigramente la data dell'elemento precedente e, una volta ripreso ("questa non è la data di oggi"), si è scusato e ha inserito la data di ieri. 1
  • Don't trust, do verify: poiché anche sui compiti più banali il comportamento dipende dal modello e dalle ottimizzazioni sui token del provider, per un professionista che lavora su software importante vale una sola regola: non fidarsi mai, ma verificare sempre (vedi Slice Sottili e Ciclo di Feedback). 1

Vedi anche: Martin Fowler, Refactoring nell'Era AI, Spec-Driven Development e Dark Factory Agentica, Slice Sottili e Ciclo di Feedback, Leading Words e Fondamentali del Software, Shift Left Security


Fonti e Riferimenti