Da dove nasce l'esperimento

A inizio maggio ho scritto che il vibe coding è un moltiplicatore di competenza, non un suo sostituto. Che il modello è una leva, che la leva amplifica quello che ci metti, e che chi non sa riconoscere l'errore produce macerie che sembrano software. Era una tesi maturata sul lavoro di tutti i giorni, ma restava una tesi. Mancava la controprova in condizioni misurabili.

Così ho scelto un banco di prova difficile apposta. Costruire da zero un sistema completo, l'emulatore di un sistema esterno reale con specifiche ufficiali da rispettare alla lettera, senza scrivere una riga di codice a mano. Solo conversazione. Se la tesi era giusta, il risultato doveva dipendere da quanto giudizio tecnico riuscivo a mettere nella guida, non dalla quantità di codice generato.

Tre giorni, sedici ore

Il risultato è arrivato in tre giorni, circa 16 ore effettive di conversazione. Più servizi che cooperano e girano insieme in docker, API REST e SOAP fedeli alle specifiche, flussi XML firmati, una console web, una libreria client e oltre 80 test automatici che passano. Non è un prototipo da demo. E non è nemmeno perfetto. Qualche bug c'è ancora di sicuro, come in qualunque software a questo stadio, perché nessun sistema nasce finito in tre giorni.

Il codice l'ha scritto tutto l'AI. Io ho parlato. E la storia di quei tre giorni dimostra la tesi di giugno meglio di quanto sperassi, perché la dimostra al contrario. Ogni passaggio critico è stato un punto in cui, senza giudizio tecnico, l'esperimento sarebbe fallito in silenzio.

Il primo scricchiolio

All'inizio fila tutto liscio. Descrivi il dominio, l'AI genera entità, endpoint, contratti. Compila, gira, le pagine rispondono. È la fase in cui ti convinci che funzioni. Ed è la fase più pericolosa, perché tutto quello che vedi è plausibile e niente di quello che vedi è verificato.

Il primo scricchiolio è arrivato dalla gestione dell'eccezioni. Qualunque cosa sbagliassi nelle prove manuali, il sistema rispondeva sempre con lo stesso codice di errore generico. Sempre quello. La specifica ufficiale però descrive una struttura precisa. Quel codice è solo la busta, e la causa vera viaggia annidata in campi dedicati. L'AI leggeva la busta e buttava via il contenuto. Il codice era pulito, compilava e mentiva. Ho chiesto se fosse davvero possibile che l'errore fosse sempre e solo quello. Non lo era. È stato il confronto con la specifica a far emergere il problema. Senza quel confronto il sistema sarebbe uscito così, e chi lo usava avrebbe imparato a gestire errori che nella realtà non esistono.

È quello che dice il contratto?

Quel bug ha cambiato la domanda. Da "funziona?" si passa a "è quello che dice il contratto?". E con la domanda giusta i casi sono usciti uno dietro l'altro.

La specifica prevedeva un flag che cambia il comportamento di un oggetto dopo una scadenza, e una regola di inclusività sulla data stessa. La prima versione sbagliava entrambe le cose. A farlo emergere non è stato un test rosso, dato che i test li scriveva la stessa IA che aveva scritto il bug. È stato il confronto con la specifica, che quei comportamenti li prevede nero su bianco.

Poi un campo obbligatorio. In uno scenario di ripartizione tra più soggetti, la specifica richiede un identificativo per ciascuno di loro. L'interfaccia non lo chiedeva. Compilavi il form, l'oggetto si creava, sembrava tutto a posto. È il bug che vede chi conosce il dominio, non chi guarda se la pagina risponde.

Poi una funzionalità centrale ridotta a decorazione. Un pezzo fondamentale del flusso era implementato in modo minimale e ignorato dal frontend. Tutto girava e tutto sembrava completo. Mancava la parte per cui il sistema esiste.

E infine la copertura. L'AI aveva implementato i campi "rilevanti" del contratto e saltato gli altri. Nell'articolo di giugno la chiamavo la soluzione mediana, quella buona per il caso generico che non è mai il tuo caso. Eccola all'opera. La copertura è stata portata al contratto intero, perché un sistema che gestisce solo i casi comodi non prepara a niente.

Questi errori hanno tutti la stessa natura. Nessun linter li segnala, nessun compilatore li ferma. Compilano, girano, sembrano corretti. Sono falsi. Sono errori di dominio, e il dominio l'IA non lo pretende da sola.

Le decisioni che non si digitano

Nel frattempo arrivavano decisioni che con il digitare non c'entrano, ma con il programmare sì.

La prima è costata cara, almeno in apparenza. L'AI aveva impostato i frontend a modo suo e gliel'ho fatti buttare e rifare in base al pattern giusto per quel contesto. Scartare codice funzionante per una questione di architettura sembra un lusso. Non lo è. È la differenza tra una demo e una base su cui altri possono leggere e costruire.

La seconda è una presa di posizione. I client non validano niente. L'utente può sbagliare e l'errore arriva dal backend, con la struttura vera, come accadrebbe contro il sistema reale. È la conseguenza diretta del principio fondante dell'esperimento. Il codice scritto contro l'emulatore deve girare identico contro l'ambiente ufficiale, cambiando solo la configurazione.

Poi i confini. Quando alla console è arrivata una funzionalità nuova, ho scelto di lavorare solo nel frontend, senza toccare il nucleo che doveva restare stabile. E dal reset completo dei dati ho tenuto fuori quelli che appartenevano a un altro servizio, perché in un sistema distribuito ogni dato ha un proprietario. Pattern, confini, proprietà dei dati. Sono scelte di architettura, e l'IA le subisce volentieri ma non le impone mai.

Pretendere la verità

A giugno scrivevo che il modello è compiacente, che ti accompagna a sbattere col sorriso. L'ho visto succedere.

A un certo punto l'AI mi ha rassicurato che il sistema era pari pari a quello reale. Le ho chiesto di tranquillizzarmi davvero oppure di dirmi la verità. La verità era un elenco di parti simulate e di scorciatoie. Quell'elenco oggi sta scritto in chiaro nell'interfaccia, perché un software che dichiara i suoi confini vale più di uno che mente.

Lo stesso vale per il metodo. Davanti ai problemi ho fermato la scrittura di codice e ho spostato il lavoro sulla diagnosi. Il lavoro è andato avanti per fasi, con un inventario dell'esistente prima di toccare qualsiasi cosa e un piano prima dell'esecuzione. E le dichiarazioni di funzionamento non bastavano. Servivano verifiche end-to-end nell'ambiente reale e test aggiunti dove mancavano.

Quello che non ho fatto

L'onestà vale anche al contrario, altrimenti questo diventa il pezzo autocelebrativo che non voglio scrivere.

Non ho scritto una riga di codice. Il codice consegnato l'ho letto, perché senza leggere sei cieco e resta la prima regola, ma non l'ho debuggato riga per riga e non ho progettato le implementazioni. Mapping, serializzazioni ed endpoint sono lavoro dell'AI. I test li ha scritti l'AI, io ho chiesto che esistessero e che girassero davvero. Una parte dei miei sì erano ratifiche di proposte sue, non decisioni mie, e approvare con giudizio conta ma pesa meno di decidere. Ho fatto anche parecchia QA visiva, icone duplicate, contrasti sbagliati, spaziature. Bug veri, ma li trova un utente attento senza saper programmare, e non li conto come prova di competenza.

I conti

Una stima a consuntivo sullo scope dice che lo stesso lavoro sarebbe costato a un programmatore senior tra le 200 e le 350 ore, da uno a due mesi a tempo pieno. Le parti che bruciano ore sono quelle meno visibili, serializzazioni, namespace, firme. Le ore di conversazione sono state 16. A giugno parlavo di un esperto dieci volte più veloce. A consuntivo, su questo progetto, il moltiplicatore sta intorno a 15. Prendetelo come ordine di grandezza, non come misura.

E i bug residui? Ci sono, è inevitabile. Usciranno con l'uso e si correggeranno con lo stesso ciclo, sessioni da minuti, non giornate di sviluppo. Quello che non cambia è la condizione. Anche quei bug avranno bisogno di qualcuno che li riconosca come sbagliati prima di poterli correggere.

Perché il moltiplicatore regge a una condizione, e una sola. I moltiplicatori amplificano quello che trovano. Se trovano qualcuno in grado di riconoscere la busta degli errori, il flag dimenticato, il campo obbligatorio assente, moltiplicano competenza. Se trovano zero, moltiplicano zero. Anzi, peggio. Producono un sistema plausibile e sbagliato, spedito in buona fede, che esplode quando l'errore costa.

Vale ancora la concessione di giugno. Gli strumenti migliorano in fretta e una parte di questo lavoro di guardia verrà assorbita dai modelli, quindi l'esperimento andrà rifatto tra qualche anno e darà numeri diversi. Ma oggi la tesi esce dall'esperimento confermata e pure rafforzata. Il vibe coding non chiede di digitare. Chiede di riconoscere quando l'output è sbagliato e di dirigere la correzione. A parità di AI, cambia la guida e cambia il risultato.