Cosa ne è stato del C?
Qualche anno fa, chi voleva diventare programmatore si sentiva dire sempre la stessa cosa: impara il C. Era il rito di passaggio. Puntatori, malloc(), segmentation fault alle due di notte. Se sopravvivevi al C, sopravvivevi a tutto.
Oggi un ragazzo che vuole iniziare apre un editor, scrive in italiano "fammi un'app che tiene traccia delle mie spese", e dopo trenta secondi ha qualcosa che funziona. Non ha mai visto un puntatore. Forse non sa nemmeno in che linguaggio è scritto il codice che ha davanti.
E allora la domanda è legittima: cosa ne è stato del C? E più in generale: ha ancora senso studiare un linguaggio di programmazione, quando il computer ha finalmente imparato a capire il nostro?
Questo articolo è per chi sta pensando di iniziare. La risposta breve è che i linguaggi contano molto meno di prima, ma la programmazione conta più che mai. La risposta lunga richiede di fare un passo indietro e capire a cosa serve, davvero, un linguaggio di programmazione.
A cosa serve un linguaggio di programmazione
Un processore non capisce il C. Non capisce Python, PHP o Java. Capisce una cosa sola: sequenze di byte che corrispondono a istruzioni elementari, del tipo somma questi due registri, salta a questo indirizzo se il risultato è minore o uguale. Tutto il resto è traduzione.
Un linguaggio di programmazione è un compromesso tra due interlocutori: l'essere umano, che ragiona per intenzioni ("voglio la somma dei numeri da 1 a 10"), e la macchina, che ragiona per istruzioni. Ogni linguaggio sposta il punto d'incontro un po' più vicino all'uno o all'altro.
Vediamo la stessa intenzione, sommare i numeri da 1 a 10, espressa a livelli diversi.
Codice macchina (x86-64). È ciò che la CPU esegue davvero. Sedici byte:
31 C0 B9 01 00 00 00 01 C8 FF C1 83 F9 0A 7E F7
Assembly. Gli stessi byte, con un nome mnemonico al posto di ogni istruzione. È una corrispondenza uno a uno: chi scrive assembly sta ancora parlando la lingua del processore, solo con parole leggibili.
xor eax, eax ; somma = 0
mov ecx, 1 ; i = 1
ciclo:
add eax, ecx ; somma += i
inc ecx ; i++
cmp ecx, 10
jle ciclo ; se i <= 10, ripeti
C. Compare il concetto di variabile e di ciclo. Il compilatore decide quali registri usare e quali istruzioni generare. Ma il programmatore pensa ancora come la macchina: un contatore, un accumulatore, un confronto.
int somma = 0;
for (int i = 1; i <= 10; i++) {
somma += i;
}
PHP o Python. Non c'è più un ciclo esplicito. Descriviamo cosa vogliamo (la somma di un intervallo), non come ottenerlo.
$somma = array_sum(range(1, 10));
somma = sum(range(1, 11))
Linguaggio naturale. L'ultimo gradino, quello che fino a pochi anni fa era fantascienza:
Calcola la somma dei numeri da 1 a 10.
Cinque modi di dire la stessa cosa. Scendendo, ogni livello è più vicino alla macchina, più veloce, più controllabile e più faticoso da scrivere. Salendo, ogni livello è più vicino all'uomo, più espressivo, e nasconde sempre più cose.
Tutta la storia dei linguaggi di programmazione è la storia di questa scala: ogni generazione ha aggiunto un gradino in cima.
Settant'anni di gradini
Ogni linguaggio importante è nato per risolvere un problema preciso. Quasi sempre, quel problema era: c'è qualcosa che il programmatore non dovrebbe più essere costretto a gestire a mano.
| Anno | Linguaggio | Cosa ha tolto dalle spalle del programmatore |
|---|---|---|
| anni '40 | Codice macchina | — si programmava con interruttori, cavi e schede perforate |
| 1949 | Assembly | ricordare i codici numerici delle istruzioni |
| 1957 | Fortran | conoscere il processore: si scrivono formule, non istruzioni |
| 1959 | COBOL | il linguaggio matematico: istruzioni simili all'inglese, per i gestionali |
| 1972 | C | riscrivere tutto per ogni macchina: il codice diventa portabile |
| 1985 | C++ | organizzare a mano programmi enormi: arrivano classi e oggetti |
| 1991 | Python | la verbosità: codice che si legge quasi come pseudocodice |
| 1995 | Java, PHP, JavaScript | la gestione della memoria, il web, il browser |
| 2000 | C# | integrare un ecosistema intero: linguaggio, runtime e librerie |
| 2015 | Rust | scegliere tra velocità e sicurezza della memoria |
| 2022 | Gli LLM | la sintassi stessa |
Il pattern si ripete
Quando nel 1957 John Backus e il suo gruppo all'IBM presentarono il Fortran, molti programmatori erano scettici. Un compilatore, dicevano, non avrebbe mai prodotto codice efficiente quanto l'assembly scritto a mano da un esperto. Avevano ragione, all'inizio. Poi i compilatori migliorarono, e nel giro di pochi anni l'assembly scritto a mano divenne una nicchia.
La stessa scena si è ripetuta a ogni gradino. Il C era considerato troppo lento rispetto all'assembly. Il garbage collector di Java era considerato un lusso per chi non sapeva gestire la memoria. PHP e JavaScript erano "linguaggi giocattolo", non roba da programmatori veri.
Ogni volta la critica conteneva un pezzo di verità, e ogni volta la storia è andata nella stessa direzione: l'astrazione vince, perché il tempo umano costa più del tempo macchina. Un processore moderno esegue miliardi di istruzioni al secondo. Un programmatore ne scrive qualche centinaio di righe al giorno. Conviene sprecare un po' del primo per risparmiare il secondo.
Gli LLM sono semplicemente il gradino successivo. Il più alto finora, e il primo in cui il linguaggio in cima alla scala non è stato progettato da nessuno: è la lingua che parliamo.
Allora, cosa ne è stato del C?
Sorpresa: sta benissimo. Non è sparito, ha solo cambiato posto.
Torniamo alla riga PHP di prima:
$somma = array_sum(range(1, 10));
array_sum() e range() non sono scritte in PHP. Sono funzioni dello Zend Engine, il motore che esegue PHP, e lo Zend Engine è scritto in C. Lo stesso vale per Python: l'interprete standard si chiama CPython proprio perché è scritto in C. Quando scrivi sum(range(1, 11)), da qualche parte un ciclo for in C sta facendo esattamente quello che avremmo scritto a mano nell'esempio in C visto prima.
E la lista continua:
- il kernel Linux, che fa girare la maggior parte dei server del pianeta e ogni telefono Android;
- SQLite, probabilmente il database più diffuso al mondo, presente in ogni smartphone e in ogni browser;
- il firmware di router, lavatrici, centraline delle auto, dispositivi medici;
- buona parte degli strumenti che fanno girare gli stessi LLM, dove C e C++ spremono fino all'ultima istruzione le schede video.
Il C non è morto. È diventato il pavimento. Ogni gradino aggiunto sopra di lui ha reso meno necessario scriverlo, ma non ha reso meno necessaria la sua esistenza. Anzi: più in alto si costruisce, più le fondamenta devono essere solide.
Quello che è cambiato è chi lo scrive. Una volta il C era il linguaggio di tutti, dal gestionale al videogioco. Oggi è il linguaggio di chi costruisce le fondamenta: sistemi operativi, motori di database, runtime, driver, sistemi embedded. È un mestiere più ristretto, più specializzato, e molto ben pagato.
Per il programmatore che inizia oggi, questo significa una cosa precisa: probabilmente non scriverà mai C per lavoro. Ma ogni riga che scriverà, in qualunque linguaggio, finirà per girare sopra del C.
L'ultimo gradino: la lingua che parliamo
Nel gennaio 2023 Andrej Karpathy, tra i fondatori di OpenAI ed ex responsabile dell'intelligenza artificiale di Tesla, scrisse una frase diventata molto citata: "The hottest new programming language is English". Il linguaggio di programmazione più di moda è l'inglese. Era una provocazione, ma descriveva bene il nuovo gradino.
Cosa è diventato quasi inutile
Per decenni una parte enorme del mestiere è stata memoria sintattica. Ricordare che in PHP strpos() vuole prima il pagliaio e poi l'ago, mentre in_array() vuole prima l'ago e poi il pagliaio. Ricordare come si apre un file in Java, come si fa una richiesta HTTP in C#, quale libreria usare per leggere un CSV in Python.
Tutto questo, oggi, un LLM lo sa meglio di qualunque programmatore. Lo sa in decine di linguaggi contemporaneamente, e traduce codice dall'uno all'altro in pochi secondi. Il valore di sapere a memoria un linguaggio è crollato, così come il valore di saper fare i calcoli a mente è crollato con l'arrivo della calcolatrice.
Chi oggi si presenta come "programmatore PHP" o "sviluppatore Java" sta definendo il proprio mestiere sulla parte che vale meno.
Cosa non è cambiato affatto
C'è però una differenza fondamentale tra un LLM e un compilatore. Un compilatore C, dato lo stesso codice, produce sempre lo stesso risultato, e se il codice è ambiguo si rifiuta di compilarlo. Un LLM accetta qualunque frase, anche ambigua, e produce qualcosa. Qualcosa di plausibile. Non necessariamente qualcosa di giusto.
Il problema è che il linguaggio naturale è terribilmente ambiguo. Prendiamo una richiesta banale:
Ordina gli utenti per nome.
Per nome o per cognome? In ordine crescente o decrescente? "Élodie" va prima o dopo "Zeno"? Maiuscole e minuscole contano? E se due utenti hanno lo stesso nome? E se sono due milioni, li ordiniamo in memoria o li chiediamo già ordinati al database?
Un LLM risponderà a tutte queste domande al posto nostro, in silenzio, scegliendo l'interpretazione più probabile. Il più delle volte andrà bene. Le volte in cui non andrà bene, solo chi sa che quelle domande esistevano si accorgerà dell'errore.
Nel 2002 Joel Spolsky formulò quella che chiamò legge delle astrazioni che perdono (The Law of Leaky Abstractions): ogni astrazione non banale, prima o poi, lascia trapelare i dettagli che dovrebbe nascondere. Il garbage collector nasconde la memoria, finché l'applicazione non esaurisce la RAM. L'ORM nasconde l'SQL, finché una pagina non esegue mille query. L'LLM nasconde il codice, finché il codice non sbaglia.
E quando un'astrazione perde, serve qualcuno che sappia cosa c'è sotto.
Cosa studiare, allora
Se la sintassi vale sempre meno, dove conviene investire il proprio tempo? Il principio è semplice: studia ciò che resta vero in qualunque linguaggio. Ciò che un LLM non può decidere al posto tuo, perché dipende dal problema e non dalla sintassi.
Come funziona la macchina
Non serve scrivere in C, ma serve sapere cosa succede quando il tuo codice gira. Cos'è la memoria e perché finisce. Perché leggere da disco è migliaia di volte più lento che leggere dalla RAM, e perché una chiamata di rete lo è ancora di più. Cosa significa che due richieste arrivano nello stesso istante.
È il motivo per cui scrivere un po' di C, anche solo per qualche settimana, resta uno dei migliori esercizi che un principiante possa fare. Non per usarlo, ma per vedere il pavimento su cui camminerà per tutta la carriera.
Strutture dati e complessità
Un LLM ti scriverà volentieri un doppio ciclo che confronta ogni elemento con ogni altro. Su cento elementi è istantaneo. Su un milione, $O(n^2)$ significa mille miliardi di confronti, e il server si ferma. Riconoscere a colpo d'occhio la differenza tra $O(n^2)$ e $O(n \log n)$, e sapere quando un array va sostituito da una mappa, è una competenza che nessuna evoluzione dei linguaggi ha mai reso obsoleta.
Architettura
È qui che si sposta davvero il mestiere. Un LLM scrive ottimamente una funzione. È molto meno bravo a decidere dove quella funzione deve stare, quali componenti devono conoscersi e quali no, dove passa il confine tra la logica di business e il database, cosa succede al sistema fra due anni quando i requisiti saranno cambiati.
Le domande architetturali — come divido il sistema, chi è responsabile di cosa, come gestisco gli errori, come scalo, come lo testo — sono identiche in PHP, in C# e in Rust. Chi le sa porre e risolvere sa programmare in qualunque linguaggio, compreso l'italiano.
Le fondamenta condivise
HTTP, SQL, le transazioni, la concorrenza, la sicurezza. Sono tecnologie vecchie di decenni, su cui poggia quasi tutto il software moderno. Un LLM che genera una query senza un indice, o un endpoint vulnerabile a una SQL injection, produce codice che funziona. Accorgersi che è sbagliato richiede di conoscere i fondamenti.
Saper leggere il codice
Il paradosso è che nell'era degli LLM si scrive meno codice ma se ne legge molto di più. Il lavoro si sposta dallo scrivere al revisionare: capire cosa fa un blocco di codice che non hai scritto tu, valutare se è corretto, scrivere i test che lo dimostrano. È la stessa abilità di un senior che fa code review a un junior. Solo che adesso il junior scrive mille righe al minuto.
Il dominio
Infine, la competenza più sottovalutata: capire il problema. Come funziona una fattura, una prenotazione, un magazzino, una pratica assicurativa. Il codice è solo la traduzione di un modello del mondo, e un modello sbagliato tradotto alla perfezione resta sbagliato. Nessun LLM conosce il tuo cliente meglio di te.
Conclusione
Il C non è morto. È sceso di qualche piano, ed è diventato il pavimento su cui poggia tutto il resto. Ogni linguaggio venuto dopo ha aggiunto un gradino alla scala, e gli LLM ne hanno aggiunto uno così alto che dalla cima quasi non si vede più il fondo.
Ma la scala c'è ancora. E il mestiere del programmatore non è mai stato conoscere un gradino a memoria: è sempre stato sapere su quale gradino ti trovi, cosa c'è sotto, e cosa succede quando qualcosa si rompe.
A chi vuole iniziare oggi, quindi, il consiglio non è più impara il C. E nemmeno impara Python. È impara a pensare come un programmatore: scomporre un problema, modellarlo, scegliere le strutture giuste, prevedere dove si romperà. Il linguaggio con cui lo esprimerai, che sia PHP, Rust o l'italiano, è solo l'ultimo dei dettagli.
Per settant'anni abbiamo imparato a parlare la lingua delle macchine. Adesso che loro hanno imparato la nostra, la parte difficile non è più farsi capire. È sapere cosa dire.