Da 0 e 1 all'AI
Quello che stiamo vedendo si iscrive in un percorso avviato fin dalla concezione dell'informatica.
Qualche giorno fa, Mark Cuban ha detto che “Software is dead because everything’s going to be customized to your unique utilization.” Ieri Grady Booch ha risposto con un tweet che riassume in poche parole quanto è accaduto negli ultimi 60‑70 anni.
Grady Booch è stato uno dei promotori dell’approccio object‑oriented e ha contribuito allo sviluppo di UML. La sua carriera si è sviluppata a cavallo tra industria e accademia, tra ricerca e applicazioni industriali.
La frase di Booch, benché telegrafica, è molto ricca di storia e contenuti:
The entire history of software engineering is one of rising levels of abstraction. That’s all we’re experiencing here: no more no less…
Come si è evoluta la storia dell’industria e dell’ingegneria del software?
Dai cavi agli 0 e 1
Agli albori, i calcolatori erano oggetti grossi e primitivi, con potenze di calcolo estremamente limitate. I programmi erano sequenze di 0 e 1 che il programmatore doveva inserire nella memoria del calcolatore, inizialmente addirittura tramite una serie di commutatori sul pannello del computer e, in seguito, tramite nastri e schede perforate.
Ancora prima di arrivare alle sequenze di bit, la “programmazione” consisteva addirittura nel cablaggio fisico della macchina: negli anni ’40, macchine come ENIAC venivano configurate collegando manualmente cavi su grandi pannelli chiamati plugboard, ispirati ai centralini telefonici. Per cambiare programma, bisognava riprogettare il flusso logico su carta, poi scollegare e ricollegare i cavi, impostare gli interruttori e, infine, testare: potevano volerci giorni per preparare un nuovo calcolo.

Scrivere anche i programmi più semplici era un’impresa. Per questo si è pensato, da un lato, di introdurre dispositivi di input/output più facili da usare (nastri, schede e poi terminali e stampanti) e, soprattutto, di ideare metodi più efficaci per spiegare al calcolatore cosa doveva fare.
Quando si usavano solo 0 e 1, il programmatore doveva compiere un salto logico enorme. Da un lato aveva un problema e un algoritmo per risolverlo, tipicamente basato su equazioni matematiche o su informazioni relative a qualche processo amministrativo o operativo. Dall’altro, tutto questo andava espresso tramite comandi codificati in 0 e 1. Oltre a essere estremamente macchinose e time consuming, queste attività richiedevano uno sforzo cognitivo enorme, perché il gap da colmare tra la definizione del problema e la sequenza di 0 e 1 era gigantesco.
Il livello di astrazione a cui ragionavano i programmatori era immensamente superiore a quello offerto dai calcolatori:
colmare il gap richiedeva un'enorme mole di lavoro.
Per questo motivo si è iniziato a pensare di sviluppare linguaggi per rappresentare i programmi più vicini al modo di ragionare del programmatore. È ciò che Booch chiama “rising levels of abstraction”.
Le generazioni di linguaggi: dal linguaggio macchina a Java (e oltre)
Un primo passaggio è stato il linguaggio assembly. Invece di vedere solo sequenze di 0 e 1, si è iniziato a usare semplici comandi testuali come “MOVE” o “ADD”, insieme a nomi simbolici per i registri e le posizioni di memoria. Il linguaggio che la macchina capisce davvero – la sequenza di 0 e 1 – si chiama linguaggio macchina e costituisce la prima generazione di linguaggi di programmazione. I linguaggi assembly, che danno una veste più leggibile alle stesse istruzioni macchina, sono considerati di seconda generazione.
Poiché il calcolatore non comprende direttamente le parole “MOVE” o “ADD”, sono stati sviluppati altri programmi (immaginate con quale sforzo a quei tempi!) che traducono tali forme più astratte in opportune sequenze di 0 e 1: gli assemblatori (in inglese, assembler). Anche qui l’idea è sempre la stessa: dare ai programmatori un linguaggio più vicino alla loro mente e spostare su un altro programma il lavoro di “parlare in bit” alla macchina.
Non contenti, i ricercatori hanno cercato di innalzare ulteriormente il livello di astrazione. È nata così la terza generazione di linguaggi di programmazione, che è cominciata con Fortran e Cobol (anni ‘50 e ‘60) e ha via via prodotto un numero enorme di linguaggi: Algol, Algol 68, Pascal, C e molti altri. Per ciascuno di essi è stato sviluppato un programma per tradurli in linguaggio macchina.
In questo contesto si sono affermati due tipi di strumenti:
I compilatori, che traducono il programma dal linguaggio considerato al codice macchina prima dell’esecuzione.
Gli interpreti, che analizzano le istruzioni una alla volta e le eseguono direttamente, invocano le operazioni necessarie sul calcolatore.
I linguaggi di terza generazione sono quelli che molti conoscono di nome ancora oggi: C, C++, Java, Python, Perl, PHP, C#, e così via. Anche approcci più recenti, come Rust, Go e Swift, vengono in genere inclusi in questa stessa categoria: sono linguaggi ancora “testuali”, ma molto più espressivi e strutturati rispetto alle generazioni precedenti.
È importante notare che in questa categoria rientrano linguaggi che hanno introdotto un paradigma di programmazione molto potente, quello a oggetti, adottato, ad esempio, in C++ e Java.
Quarta e quinta generazione: settoriali, dichiarativi, intelligenti
I linguaggi di quarta generazione sono orientati a ottimizzare alcune specifiche forme di programmazione. Per esempio, SQL serve a elaborare in modo dichiarativo le basi di dati. “Dichiarativo” perché, mentre la maggior parte dei linguaggi precedenti richiede di indicare la sequenza di operazioni da eseguire per ottenere un risultato, in SQL si descrive “che cosa” si vuole ottenere, e non “come” ottenerlo: il sistema che analizza SQL deriva la sequenza di operazioni da eseguire per arrivare a quel risultato.
In realtà, in questa categoria rientrano linguaggi anche molto diversi tra loro, ma che in qualche modo sono più settoriali e specializzati rispetto ai precedenti, che si possono considerare “general purpose”. Per esempio, MATLAB e R sono talvolta classificati in questa categoria, come linguaggi di alto livello specializzati nel calcolo numerico e statistico.
Già negli anni ‘50, ‘60 e ‘70 sono nati linguaggi funzionali e logici come Lisp (1958-60) e Prolog (1972), proprio usati per applicazioni di intelligenza artificiale ed elaborazione simbolica. Oggi spesso sono detti di quinta generazione. Essi rappresentano un’ulteriore evoluzione dei linguaggi di programmazione visti in precedenza: Prolog, in particolare, esprime problemi in termini di fatti, regole e vincoli, e lascia al sistema il compito di “dedurre” le soluzioni. Anche qui ritroviamo il tema dell’astrazione: invece di descrivere passo per passo l’algoritmo, si dichiarano i vincoli che la soluzione deve soddisfare.
Questa evoluzione è continua e si arricchisce periodicamente di nuove tecnologie che proseguono nello sforzo di elevare il livello di astrazione offerto al programmatore. Di conseguenza, aumenta la complessità del processo di traduzione da “quello che vede il programmatore” a “quello che la macchina capisce davvero”.
Questa è la storia a cui fa riferimento Booch.
L’AI non è estranea a questo percorso
Il compito del progettista/programmatore è capire un problema, identificare una strategia di soluzione (un algoritmo) e quindi scrivere un programma che lo implementi. Questo programma sarà poi compilato o interpretato per essere eseguito dal calcolatore.
Più il linguaggio che devo usare per scrivere un programma si allontana dai bit e si avvicina al modo di ragionare della persona, più semplice è il suo compito. Più questo linguaggio si allontana dai bit e dai circuiti del computer, maggiore è il suo livello di astrazione.
Nei decenni abbiamo percorso proprio questo: siamo passati dallo scrivere 0 e 1 a linguaggi artificiali semplici ma strutturati, come Java.
La sfida di cui si sta parlando oggi è se sia possibile innalzare ulteriormente il livello di astrazione e permettere ai programmatori di usare la lingua parlata come linguaggio di programmazione. Invece di scrivere codice, dire alla macchina qualcosa come: “Costruisci un’app che prenda questi dati di vendita e mi mostri ogni giorno un grafico aggiornato”. L’obiettivo è permettere al programmatore di esprimersi in inglese (o in italiano!) e affidare alla “macchina” il compito di interpretare il significato delle parole e di generare ciò che serve per eseguire le operazioni sul calcolatore. È quello che oggi spesso chiamiamo “vibe coding”. È un salto notevole e introduce una discontinuità rispetto al passato.
Qual è l’aspetto critico? La lingua parlata è ambigua. I linguaggi discussi in precedenza sono molto semplici rispetto all’italiano o all’inglese, ma hanno una sintassi e una semantica definite in modo puntuale, in modo che un interprete o un compilatore possa elaborarli senza dubbi. Nel linguaggio naturale, invece, frasi brevi come “fammi un report delle vendite” aprono mille domande: di che periodo? Con quale dettaglio? Per quale pubblico? Questa ambiguità rende la traduzione automatica molto più difficile.
E in ogni caso, se anche funzionasse perfettamente, restano due problemi fondamentali, a monte e a valle:
A monte, rimane la necessità di comprendere nel dettaglio la natura del problema e le modalità in base alle quali ha senso risolverlo.
A valle, bisogna verificare se quanto prodotto risponde effettivamente ai bisogni, è affidabile, sicuro, strutturalmente solido e presenta prestazioni adeguate.
Inoltre, il processo va reso ripetibile e gestibile nel tempo: le applicazioni evolvono e devono essere integrate con altre componenti informatiche che possono variare nel corso di anni e lustri.
Tutti i ricercatori degli ultimi 60‑70 anni hanno cercato di innalzare il livello di astrazione. Se con GenAI ci riuscissimo davvero, sarebbe un successo straordinario. Ma non è semplice né così ovvio dire “Eureka!”.
Ancora non siamo lì.
P.S.: Ho fatto rileggere il mio post originario a Perplexity Pro, che mi ha dato diversi suggerimenti utili per migliorarne la leggibilità.
Nel complesso il pezzo funziona molto bene per un pubblico non tecnico: il filo logico è chiaro, l’uso della citazione di Booch è centrato e l’idea dei “livelli di astrazione” è spiegata in modo accessibile. Ti segnalo alcune aree dove puoi migliorare chiarezza, precisione e ritmo, con suggerimenti operativi.
Grazie!





Grazie, sono tornato indietro di cinquant’anni, quando leggevo i primi libri sui codici binari, poi sono approdato al Fortran e usato Ansys ma ho preso un’altra strada…
Mi ha fatto ripercorrere la mia esperienza di sviluppatore di modelli matematici (il mio campo è stata la termodinamica, la cinetica chimica e la modellazione reattoristica). Credo di aver usato l’assembler sul Vic 20; le schede perforate invece le ho usate per l’esame dí programmazione di calcolatori elettronici nel 1979 (un programmino in Fortran per trovare l’uscita più corta da un labirinto) e per la tesi; poi nei 40 anni successivi Fortran, BASIC, ho persino comprato un libro sul Prolog, era l’epoca in cui erano di moda i Sistemi Esperti. Un po’ di C++ e Java. Ci fu poi l’Aspen Custom Modeler, software commerciale per generare modelli di processi chimici: con un’opportuna sintassi si scrivevano le equazioni e si dichiaravano le variabili senza preoccuparsi della loro risoluzione. Verso la fine della mia attività lavorativa, Matlab e Python per cinetiche e modellazione dei dati sperimentali.
Mi dispiace un po’ di essere andato in pensione prima dell’avvento degli LLM.