Project
Walden — spec-driven development vs vibe coding
dalla paura di volare al FADEC, e a come il rigore dell’aviazione è diventato un delivery kernel open source che si chiama Walden

Dal vibecoding al software che conta
Se stasera chiamassi mia madre per chiederle cosa sta preparando per cena, non mi stupirei più di una risposta del tipo: «Niente di che, sto facendo un nuovissimo sistema di memoria per gli agenti, commenta agente e ricevi subito la mia guida definitiva».
No, non è un futuro distopico alla Black Mirror, siamo a giugno 2026!
Perché è questo il punto: la chimera del “che ce vo’, lo faccio in 5 minuti” attanaglia le nostre teste da diverso tempo, e purtroppo dobbiamo subire queste angherie anche da quelli che non hanno mai scritto una riga di codice, li riconosco subito, hanno la faccia di chi ha scoperto la verità e indossano fieri la maglietta di Dunning-Kruger, poveracci, stanno solo vibe codando.
F E R O C E, sì leggilo staccato perché suona meglio, più lento. È così che divento quando incontro queste figure mitologiche.
Karpathy, non proprio l’ultimo scemo, dice che il vibe coding è quell’arte in cui ci sediamo davanti alla macchina e col linguaggio naturale seguiamo il flusso, accettando passivamente quello che la tecnologia ci propone. Ecco: tra quella modalità e il software che portiamo in produzione c’è un abisso, perché noi, da professionisti, in produzione ce lo portiamo davvero. E oggi vi racconto di che abisso si tratta.
Hai presente quando attivi la cancellazione attiva del rumore e tutto intorno diventa incredibilmente più chiaro e silenzioso? Con il mio team proviamo a fare questo in azienda, proviamo ad eliminare il rumore incredibile che ogni giorno ci piove addosso: “MCP è morto, il modello 4B che uccide i frontier, LLM Wiki ammazza il RAG…” E potrei continuare ore. È proprio questa la fatica, eliminare il rumore e creare spazio per le cose che funzionano.
Il mio di pattern, è sempre il solito: la paura di qualcosa unita agli incredibili sforzi che metto nello studio, alla fine, mi porta a costruire degli strumenti.
La fatica in questi casi non è opzionale, è l’unica cosa che conta.
E allora partiamo da lì. Dalle mie ansie del 2026, e dalla fatica provata negli ultimi tempi.
Le mie due ansie del 2026
La prima — e ci tengo, ci tengo proprio — è il ritardo perpetuo di GTA 6. Rockstar l’ha annunciato nel 2022, ci ha speso due miliardi, sarà il capolavoro dell’ingegneria del software moderna, e io ho già avvisato i miei capi che quando uscirà sparirò per due giorni, per assaporare il gioco che mi ha catapultato nell’industria videoludica e del software. La paura è che Rockstar continui a rimandare l’uscita…
La seconda invece è seria: ho paura dell’aereo. Me la porto dietro da quando ho provato l’ebbrezza di volare sulla tratta più pericolosa al mondo per raggiungere l’Himalaya, e lì ho capito che avrei avuto paura per sempre.
L’ultimo viaggio era il FOSDEM, a Bruxelles. Tutto il giorno piantonato nella dev room sull’intelligenza artificiale, e al ritorno, seduto al gate con le mani che iniziavano a sudare, ho fatto il pensiero che me le ha fatte sudare il doppio: nel 2050 saliremo su quell’aereo sapendo che quel software, quasi certamente, sarà stato scritto da una macchina.
E se il nuovo codice sorgente è il linguaggio umano, come farò io a stare a diecimila metri per aria ed essere tranquillo?
Così ho iniziato a scavare nel mondo del software safety-critical. E ho scoperto che esiste il FADEC. Io non ne sapevo assolutamente niente.
Full Authority
FADEC sta per Full Authority Digital Engine Control: una scatola che vive dentro i motori dell’aereo e governa tutto quello che ci accade. Un sistema a eventi complessissimo, safety-critical, ridondato — ogni motore ne porta due — e Full Authority significa esattamente quello che sembra: i piloti non possono interagirci manualmente, nemmeno con controlli meccanici.
Ti fidi, e voli.
E quindi la domanda: ma come sono fatti i requisiti di una cosa così? Perché sbagliare un requisito su un sistema del genere significa che l’aereo cade.
Funziona così: ogni singola riga di codice del FADEC ha un requisito esplicito, tracciato matematicamente nel codice, testato e firmato da un ingegnere. C’è una firma. Lo standard di certificazione si chiama DO-178C, e se non esiste questo rigore nelle specifiche legate al codice, la certificazione non la prendi e l’aereo non vola.
Non vola. Punto.
Praticamente l’opposto di come scrivo software io ogni giorno :D .
I requisiti impliciti sono bombe a orologeria: «io te l’avevo detto che quel bottone doveva aprire quel pop-up», «ma come, non lo sai? è ovvio». Su una webapp che fa CRUD questi errori ci possono pure stare. Su un software del genere non possono esistere e le volte che è successo, è finita malissimo.
Il nuovo codice sorgente
C’è una concettualizzazione di Andrej Karpathy (che ci saluta dal suo yacht milionario indossando la maglia di Anthropic) che inquadra perfettamente il momento.
Software 1.0: abbiamo faticato a imparare linguaggi e a costruirci sopra framework, ed era il modo in cui abbiamo sempre scritto codice.
Software 2.0: le reti neurali, che con l’ingestion di quantità enormi di dati ci hanno traghettato verso il 3.0. E nell’era del software 3.0 il codice sorgente non è più quello a cui siamo abituati: è il linguaggio naturale.
Quando il linguaggio naturale diventa il codice sorgente, la vulnerabilità non è più nel codice. È nel linguaggio umano.
Pensiamoci. Quando andiamo al bar — «un caffè, grazie» — siamo sicuri che la persona dall’altra parte capisca perfettamente il contesto, perché nel linguaggio umano c’è tantissimo di non verbale, e diamo per scontato che chi ascolta riempia i vuoti. Il linguaggio umano è incredibilmente ambiguo: per questo è affascinante, e per questo ci litighiamo sopra. Ma oggi con quel linguaggio ci parliamo alle macchine. È la differenza tra passare un payload JSON destrutturato sperando che chi lo riceve indovini i tipi, e usare uno schema rigorosamente tipizzato. Solo che adesso il payload destrutturato sono le nostre frasi.
E i numeri sono questi: circa il 56% dei difetti nel software deriva dalle specifiche. Non dal codice. Questi sistemi oggi scrivono migliaia di righe in ore, e la curva METR dice che la capacità raddoppia ogni sette mesi: oggi task da cinquanta minuti, tra poco task da settimane. Il collo di bottiglia si è spostato: non è più la velocità con cui generiamo il codice, è specificare esattamente cosa vogliamo far fare alla macchina.
Più velocità, meno contesto: e questa cosa, sinceramente, mi ha riempito d’ansia. Perché io me le sono trovate davanti, le pull request da venti, trentamila righe generate senza spec driven, senza l’arte di sedersi col team e descrivere esattamente cosa vogliamo fare. Lì nasce il debito cognitivo: non siamo più padroni non tanto di cosa è scritto — già vedersi ventimila righe è disumano — ma di come il software si sta comportando.
E se i presupposti sono agenti che lavoreranno per ore e poi per settimane, ditemi: chi è che dorme sonni tranquilli sapendo di aver lasciato un agente a lavorare da solo per sei ore?
Io no. E quando qualcosa mi fa paura, avete capito come va a finire.
Quando, chi, cosa
Quando ho iniziato a sviluppare ero un ragazzino — otto anni — e per me l’importante era sedermi davanti alla macchina e vedere che succedevano delle cose. C’era una traduzione dal linguaggio umano al linguaggio macchina, la macchina faceva cose, e per me andava bene così. Poi questa è diventata la mia professione, e ho capito l’importanza di un requisito ben scritto: è la base della nostra industria, è quello che ci permette di vendere il software.
Allora, cos’è un requisito esplicito? Risponde a tre domande, sempre le stesse.
- Quando — in quale condizione o evento si attiva il comportamento.
- Chi o cosa — il sistema, l’utente, un componente specifico.
- Fa cosa — il comportamento atteso, osservabile e verificabile.
Tre domande semplici. Se manca anche solo una delle tre, quello non è un requisito: è un’aspirazione. Sembra ovvio detto così — poi andate a leggere i ticket di Jira della vostra azienda. «Il caricamento deve essere veloce.» «L’interfaccia deve essere intuitiva.» Veloce è relativo: sotto quale latenza? Con quanti utenti? Intuitivo per chi? E in caso di errore? Boh, vedremo. Dietro parole così vaghe si nascondono decisioni architetturali enormi, che qualcuno prenderà nel momento peggiore: sotto pressione, con la produzione giù.
Un linter per il linguaggio naturale
E qui arriva EARS, che tra l’altro ha una storia bellissima.
Siamo dentro Rolls-Royce, a Derby, in Inghilterra. Alistair Mavin e il suo team — con Wilkinson, Harwood e Novak — iniziano a studiare i manuali di requisiti dei controlli motore: anni di requisiti scritti in inglese naturale, anni di cose andate storte. E in quel campo, “cose andate storte” significa disastri. Cosa trovano? Clusterizzano otto modi di scrivere male lo stesso requisito, otto pattern noti che si ripetono ovunque: l’ambiguità («il sistema dovrebbe essere veloce» — la metto nella top delle cose che leggiamo ancora oggi), la vaghezza («gestire correttamente gli errori», che non significa niente), la complessità (frasi con quattro subordinate annidate), le ipotesi implicite (condizioni d’uso mai dichiarate — per me la peggiore), le omissioni (casi limite e fallback mancanti), la duplicazione, i requisiti multipli compressi in un’unica frase, e gli acronimi non definiti. Nel 2009 pubblicano il paper che presenta EARS — Easy Approach to Requirements Syntax — l’antidoto a tutti questi problemi: la lingua inglese ridotta al minimo, vincolata a una manciata di template, per scrivere requisiti senza ambiguità, comprensibili dal reparto tecnico e tracciabili.
Quando l’ho scoperto ho pensato: questa cosa è geniale, ma è poco spinta, poco sponsorizzata. E quello che ne esisteva nel mondo AI erano banalmente delle skill — «scrivi in formato EARS, comportati da ingegnere dei requisiti» — il cui risultato, però, non era deterministico. Non poteva esserlo: gli agenti, per definizione, non lo sono.
Teniamola da parte, questa osservazione: ci torniamo tra poco.
L’analogia che secondo me funziona meglio: EARS è un linter per il linguaggio naturale. Un linter analizza il codice e trova gli errori prima dell’esecuzione. EARS fa lo stesso con la lingua: trova le ambiguità prima che diventino software.
Sei forme, una regola
Vediamole. A sinistra il FADEC, a destra la nostra vita da dev.
- Ubiquitous — l’invariante, il sempre vero, in qualsiasi stato o evento.
Il sistema SHALL <comportamento>. FADEC: il rapporto combustibile/aria non supera mai i limiti certificati, in nessuna condizione operativa. Vita da dev: ogni risposta API è JSON, conContent-Type: application/json. - Event-driven — scatta dopo un evento.
WHEN <evento>, il sistema SHALL <risposta>. FADEC: WHEN il sensore rileva un superamento della soglia di redline, interrompi l’iniezione di carburante entro venti millisecondi. Qui non c’è ambiguità: non esiste. Vita da dev: WHEN l’utente clicca Invia, valida il form e mostra gli errori inline. - State-driven — il loop: attivo finché una condizione persiste.
WHILE <condizione>, il sistema SHALL <risposta>. FADEC: WHILE il motore è in decollo, mantieni N1 entro lo 0,5% del target certificato. Vita da dev: WHILE la richiesta HTTP è in corso, mostra l’indicatore di caricamento. Sembra una sfumatura, ma è quello che distingue uno spinner che si chiude correttamente da uno che gira all’infinito. - Optional — dipende dalla presenza di una feature.
WHERE <feature>, il sistema SHALL <risposta>. FADEC: WHERE è montata l’inversione di spinta, abilita i reverser solo con le ruote a terra. Vita da dev: WHERE il 2FA è attivo, chiedi l’OTP dopo la password. - Unwanted behavior — la gestione degli errori, la cosa più catastrofica della nostra industria.
IF <condizione errata>, THEN il sistema SHALL <risposta sicura>. FADEC: IF un sensore di portata carburante restituisce un valore fuori scala, THEN commuta sul backup e invia un alert. Vita da dev: IF il database non risponde entro cinque secondi, THEN restituisci un 503 con messaggio standard. - Complex — la più complicata: stato ed evento insieme.
WHILE <condizione>, WHEN <evento>, il sistema SHALL <risposta>. FADEC: WHILE il motore è in crociera, WHEN la temperatura EGT supera gli 850°, riduci il flusso di carburante dell'8% e notifica l’FMS. Vita da dev: WHILE l’utente è autenticato come admin, WHEN accede a Utenti, mostra i dati completi con le opzioni di modifica.
Le forme una accanto all’altra hanno in comune una cosa: zero ambiguità. E scrivere i requisiti così ci permette di fare un design, e poi dei task, che non siano ambigui a loro volta.
Però guardate bene la quinta, perché i documenti veri si innamorano dell’happy path e ignorano i disastri — e quella dimenticanza causa la maggior parte dei down in produzione. Senza un IF/THEN esplicito, un’AI davanti a un errore può inventarsi un loop infinito di retry e tirarvi giù tutto.
E poi c’è la regola d’oro, quella da portarvi a casa anche se vi addormentate sul resto: se non riuscite a scrivere il vostro requisito in una di queste forme, il requisito non è abbastanza chiaro. Con una postilla severissima: un solo comportamento per criterio di accettazione. Vietata la congiunzione «e». Non potete scrivere «il sistema deve salvare i dati e inviare la mail». Mai. Perché tra sei mesi un test fallisce, e voi non sapete se è saltato il database o l’SMTP. Due requisiti indipendenti, diagnosi immediata.
Nell’epoca in cui stiamo tutti vibecodando, questa roba è oro: dettare uno standard per agenti che lavoreranno ore e giorni è la base su cui il nuovo software solido deve poggiare.
Andai nei boschi
OK, la teoria è bellissima. Ma quando sono andato a guardare i framework spec-driven che esistono oggi, ho trovato una debolezza che li accomuna: si sono fatti trascinare da una cosa che abbiamo subito tutti. L’adozione massiva di questi strumenti è stata calata dall’industria nascondendo il costo reale dell’inferenza — e non doverci preoccupare del costo ci ha consegnato sistemi che producono tonnellate di markdown che nessuno guarderà mai. «Sì, ma i markdown mica li leggi tu», mi dicono. Vero. Però io sono convinto di una cosa: i token saranno il nuovo kilowattora. E allora i sistemi vanno progettati per non essere dispendiosi.
Walden nasce esattamente da qui — e ve l’avevo detto, il mio pattern è sempre quello: l’ansia dell’aereo mi ha portato al FADEC, l’ansia degli agenti lasciati soli per ore mi ha portato a costruire uno strumento. L’obiettivo: rendere lo spec-driven incredibilmente snello. Ma deterministico.
Il nome no, quello non è ansia. Sono cresciuto a pane e Thoreau — per quello si chiama Walden. Walden, ovvero vita nei boschi custodisce la riga che uso come bussola: «Andai nei boschi perché desideravo vivere con consapevolezza, per affrontare soltanto i fatti essenziali della vita». Mio nonno me lo aveva insegnato molto prima che io trovassi le parole per dirlo: fare meno cose, ma farle con piena attenzione. Il software, di suo, fa l’esatto contrario.
Tecnicamente è uno spec-driven delivery kernel: una CLI in Go, zero dipendenze, open source sotto Apache-2.0. Tra un’idea e la produzione c’è un mare — noi partiamo dall’idea, «sviluppami una todo app», la diamo all’agente, e quello che esce non lo possiamo mettere in produzione, perché in mezzo negli ultimi quarant’anni abbiamo sempre fatto tante altre cose. Walden introduce un gate obbligatorio su ogni fase: Requirements, Design, Tasks, Execute. La skill insegna al modello a usare la CLI e scrive i requirements in formato EARS; ma siccome il modello non è deterministico, non abbiamo la certezza che lo siano davvero — e qui subentra la CLI, che in modo deterministico li valida, con un parser, contro quei template. Requisiti validi? Si passa al design, dove ogni requisito viene esploso con i suoi acceptance criteria. Design approvato? Task, dove ogni foglia cita gli ID dei criteri (R1.AC1, R1.AC2…) e porta i suoi verification command. Solo alla fine, il codice. È il modo CLI-più-LLM che sta diventando un cittadino di prima classe, come lo è stato MCP.
Niente più «pensavo fosse ovvio».
Attrito intenzionale
E se a metà strada cambio idea? Arrivati ai task dico: no, niente database relazionale, voglio NoSQL. Tutta la catena diventa stale. Che significa? Che ti rifai i requirements, li riapprovi, ti rifai il design per la parte che hai invalidato, e ti rifai i task. La coerenza è garantita da una catena di timestamp: ogni documento a valle, quando viene approvato, si annota l’approved_at del documento a monte, e se a monte qualcosa cambia, a valle niente combacia più — il sistema si blocca finché un walden reconcile non riallinea tutto.
Questa — l’ho presa da Apple — è una feature, non è un bug :) .
E qui c’è il punto che sottolineo col sangue: la cosa che non dobbiamo fare è accettare tutto quello che dice il modello, sempre, a morte. Quello equivale a fare vibe coding strutturato. Tutte queste fasi di approvazione sono volutamente meccaniche, mettono frizione apposta: servono a rendere l’umano responsabile dei requisiti e dei design che ha firmato. Perché il nostro lavoro non sarà più stare lì a scrivere il codice: sarà tornare a riunirci intorno a un tavolo coi colleghi, decidere le specifiche, scrivere il design e i task — magari col modello più grande che abbiamo, perché quella è la parte complicata — e poi far girare i task.
Human in the Reasoning
C’è un’altra cosa che ho dovuto smontare: il caro vecchio human in the loop. Se gli agenti lavoreranno per settimane, human in the loop significa che quello che è stato prodotto lo vedi dopo una settimana. Non è verosimile — soprattutto nella fase di requisiti, design e task. Da un paper della Peking University del 2023 arriva il concetto giusto: Human in the Reasoning — l’umano dentro il flusso di ragionamento, non alla fine.
In Walden si chiama Decision Checkpoint Protocol, e il cuore è un bifurcation test: mentre scrive, se l’agente incontra una decisione che si discosta troppo dall’obiettivo, si ferma e fa una domanda all’umano. L’esempio che uso sempre: devo andare a Roma. Se l’agente imbocca la Roma-Firenze, è troppo lontano dal goal — si ferma e chiede: «sei proprio sicuro che dobbiamo prendere questa strada?». Se la deviazione sono settecento metri, decide da solo e va dritto. Nel documento resta un marcatore esplicito — [decision: <la domanda>] — e, se gli dite di decidere lui, un <!-- assumed: ... --> nero su bianco.
E poi c’è la memoria, perché se rispondo a un checkpoint e al giro dopo l’agente mi rifà la stessa domanda, ho solo spostato la frustrazione. Il debito cognitivo va trasformato in capitale cognitivo, e Walden lo fa con due file che vivono dentro git, nella directory .walden. La Constitution: le regole dure del software — stack tecnologico, convenzioni, decisioni architetturali — cose che tecnicamente non cambiano, ma possono evolvere col sistema. E le Lessons: il registro degli errori che l’agente commette, strutturato in tre parti — il trigger (cosa è successo), la lesson (il pattern da evitare), il guardrail (cosa controllare la prossima volta) — che la skill rilegge prima di ogni lavoro non banale. Il sistema impara dagli errori fatti e prova a non rifarli.
E qui non sono il solo a dirlo. Erik Schluntz, da Anthropic, in Vibe coding in prod dice cose che potrei firmare riga per riga: i merge da ventiduemila righe sopravvivono in produzione perché prima è stata fatta la pianificazione, con checkpoint verificabili — non «il codice è giusto?» ma «il sistema si comporta correttamente?». E Karpathy intanto ha fatto join in Anthropic dicendo testualmente che entra per risolvere i problemi degli agenti: io mi aspetto che nel giro di pochi mesi ci pioveranno addosso degli standard — per gli orchestratori, per le memorie — come è successo con MCP e con le skill. E per fortuna: perché stiamo bruciando token per riscrivere tutti quanti la stessa cosa.
Funziona?
Perché inserire l’ennesima tabella di benchmark come presentazione nel README.md? L’incredulità di San Tommaso deve essere il vostro spirito guida, in questa giungla di open source vibecodato!
PROVATELO VOI.
git clone https://github.com/andrearaponi/walden.git
cd walden
./setup.sh
Lo script compila il binario, lo installa in ~/.local/bin/walden e vi propone la skill per Claude Code, Codex o Copilot (in azienda lo usiamo su Copilot: è agnostico, oggi usiamo un coding agent, domani ne useremo un altro). Poi si fa così:
/walden Sviluppiamo una todo app personale, monoutente: CRUD sui task, categorie,
scadenza, priorità, mark as completed. Golang 1.26, TDD fail-fast, SQLite,
OpenAPI con RFC 7807 problem details, frontend embedded in Go con React e
Tailwind. Autenticazione out of scope.
Si accende la skill, trova il binario, inizializza il repo con Constitution e Lessons da riempire, e parte il giro che ormai conoscete: requirements in EARS, validate della CLI, bifurcation test («come modelliamo la soglia di priorità?» — enum; «le categorie?» — entità CRUD), approvazione, design, ancora validate, task con i verification command. Risultato: 11 requirements, 59 acceptance criteria tutti ben formati — 33 event-driven, 16 unwanted behavior, 10 ubiquitous — zero warning. Il modello difficilmente sbaglia; quando sbaglia, la CLI solleva l’errore, e nell’errore c’è scritto come risolverlo. È un parser, no?
La pianificazione fatela col modello di reasoning più forte che avete, perché è la parte critica. L’esecuzione no: quando l’agente fa execute si legge requisito, design, task e verification — l’implementazione diventa una mera traduzione in codice, e può farla un modello più debole, magari locale se potete permettervi una macchina carrozzata.
Funziona.
E i bug? Sono l’uomo delle bold opinion: se il fix non cambia né requisiti né design — un listener rotto su un bottone — niente cerimonia, si corregge e basta. Se invece per sistemarlo devi toccare i requisiti, il bug non era nel codice :) .
Ah, e Walden in realtà è progettato per essere orchestrato: un agente che prepara il contesto, un executor che implementa, un terzo che fa la code review. Ma questa è roba per altri due, se non tre, articoli. È tutto qui — niente bestione enterprise. Letteralmente un kernel.
Nessun aereo è mai stato vibecodato
Cosa portarvi a casa, nell’ordine in cui serve. Primo: il collo di bottiglia non è più scrivere codice, è avere ben chiaro cosa vogliamo fare. Secondo: EARS è il linter del linguaggio naturale — sei forme, e bastano. Terzo: il nostro mestiere sta scivolando da scrivere codice a progettare e verificare sistemi — la velocità è della macchina, la precisione logica resta roba nostra. Quarto: il debito cognitivo diventa capitale cognitivo solo se lo strutturate — Constitution, Lessons, guardrail.
Il vibe coding — sedersi davanti alla macchina e accettare passivamente il flusso — è incompatibile con i sistemi che devono restare in piedi nel tempo. Servono requisiti, serve EARS, servono gate e checkpoint. Serve uno sforzo cognitivo strutturato. Altrimenti costruiremo soltanto, sempre più in fretta, infrastrutture di carta.
Per fortuna nessun aereo è mai stato vibecodato. E spero il vostro prossimo progetto anche.
Walden è giovane e la roadmap è lunga: se avete critiche, dubbi o suggerimenti, scrivetemi pure.
Andai nei boschi.
