Le verità sul software di Joel Spolsky restano utili anche quando l'IA scrive codice velocemente. Scopri come mantenere focus su test, assunzioni e semplicità per garantire correttezza.

L'IA può produrre codice che sembra funzionare in pochi minuti. Questo cambia il ritmo di un progetto, ma non cambia ciò che rende il software di successo. Le lezioni delle “verità sul software” di Joel Spolsky non riguardavano la velocità di digitazione. Riguardavano il giudizio, i cicli di feedback e il non auto-infliggersi complessità.
Ciò che è cambiato è il costo di creare codice. Puoi chiedere tre approcci, cinque varianti o una riscrittura completa e ottenere qualcosa immediatamente. Ciò che non è cambiato è il costo di scegliere l'approccio giusto, verificarlo e conviverci per mesi. Il tempo risparmiato nello scrivere spesso si sposta nel decidere cosa intendevi, validare i casi limite e assicurarsi che la vittoria rapida di oggi non diventi la tassa di manutenzione di domani.
Correttezza, sicurezza e manutenibilità richiedono ancora tempo reale perché si basano sulla prova, non sulla fiducia. Un flusso di login non è finito quando compila. È finito quando rifiuta in modo affidabile input errati, gestisce stati strani e non perde dati. L'IA può sembrare sicura pur mancando un dettaglio cruciale, come un controllo di permessi su un endpoint o una race condition nell'aggiornamento di un pagamento.
L'IA è più utile quando la tratti come una macchina per bozze veloci. Brilla su boilerplate, pattern ripetitivi, refactor rapidi ed esplorazione di opzioni da confrontare fianco a fianco. Usata bene, comprime la fase del "foglio bianco".
L'IA danneggia più quando le passi obiettivi vaghi e accetti l'output alla lettera. Gli stessi schemi di errore tornano: assunzioni nascoste (regole di business non dichiarate), percorsi non testati (gestione errori, retry, stati vuoti), errori sicuri (codice plausibile ma sbagliato in modo sottile) e soluzioni “ingenue” difficili da spiegare dopo.
Se il codice è economico, la nuova risorsa scarsa è la fiducia. Queste verità contano perché proteggono quella fiducia: con gli utenti, con i colleghi e con il tuo futuro sé.
Quando l'IA può generare una feature in pochi minuti, è allettante considerare il testing la parte lenta da eliminare. Il punto di Spolsky vale ancora: la parte lenta è dove sta la verità. Il codice è facile da produrre. Il comportamento corretto no.
Uno spostamento utile è trattare i test come requisiti eseguibili. Se non riesci a descrivere il comportamento atteso in modo verificabile, non hai finito di pensare. Nel lavoro assistito dall'IA questo conta più, non meno, perché il modello può produrre con sicurezza qualcosa che è solo leggermente sbagliato.
Inizia testando le cose che farebbero più male se si rompessero. Per la maggior parte dei prodotti sono i flussi core (registrazione, checkout, salvataggio, esportazione), i permessi (chi può vedere, modificare, cancellare) e l'integrità dei dati (no duplicati, totali corretti, migrazioni sicure). Copri poi i bordi che causano incidenti notturni: input vuoti, testi lunghi, fusi orari, retry e confini esterni instabili come pagamenti, email e upload di file.
L'IA è ottima nel proporre casi di test, ma non può sapere cosa hai promesso realmente agli utenti. Usala come partner di brainstorming: chiedi casi limite mancanti, scenari di abuso e combinazioni di permessi. Poi fai il lavoro umano: abbina la copertura alle tue regole reali e rimuovi i test che “testano l'implementazione” invece del comportamento.
Rendi i fallimenti azionabili. Un test fallito dovrebbe dire cosa si è rotto, non mandarti in una caccia al tesoro. Mantieni i test piccoli, nominali come frasi e rendi i messaggi d'errore specifici.
Immagina di costruire una semplice app di “note di team” con aiuto dell'IA. Le schermate CRUD arrivano in fretta. Il rischio di correttezza non è l'interfaccia. È il controllo degli accessi e le regole sui dati: un utente non deve vedere le note di un altro team, le modifiche non devono sovrascrivere cambi più recenti e eliminare una nota non deve lasciare allegati orfani. I test che bloccano queste regole sembreranno il collo di bottiglia, ma sono anche la tua rete di sicurezza.
Quando il testing è il collo di bottiglia, impone chiarezza. Quella chiarezza è ciò che impedisce al codice veloce di trasformarsi in bug veloci.
Una delle verità più durature è che il codice semplice vince su quello furbo. L'IA rende allettante accettare astrazioni sofisticate perché arrivano rifinite e veloci. Il costo si vede dopo: più posti dove i bug possono nascondersi, più file da scorrere e più momenti del tipo "che cosa fa questo?".
Quando il codice è economico, la complessità è ciò per cui paghi. Un design piccolo e noioso è più facile da testare, modificare e spiegare. Questo conta ancora di più quando la prima bozza è venuta da un modello che può sembrare sicuro pur essendo sottilemente sbagliato.
Una regola pratica è mantenere funzioni, componenti e moduli abbastanza piccoli da poter essere revisionati da un collega in minuti, non in ore. Se un componente React necessita di più custom hook, una macchina a stati locale e un layer generico di “smart renderer”, fermati e chiediti se stai risolvendo un problema reale o stai accettando architettura perché l'AI l'ha proposta.
Alcuni “test di semplicità” ti aiutano a resistere:
I prompt contano qui. Se chiedi “la migliore architettura”, spesso ottieni una sovraprogettazione. Chiedi vincoli che spingano verso meno parti mobili. Per esempio: usa l'approccio più semplice con il minor numero di file; evita nuove astrazioni a meno che non rimuovano duplicazione in tre o più punti; preferisci codice esplicito a helper generici.
Un esempio concreto: chiedi all'IA di aggiungere accesso basato su ruoli a una pagina admin. La versione furba introduce un framework di permessi, decorator e una DSL di configurazione. La versione semplice verifica il ruolo utente in un punto, limita le rotte in un unico punto e registra gli accessi negati. La versione semplice è più facile da revisionare, testare e interpretare correttamente.
Se stai costruendo in uno strumento chat-based come Koder.ai, la semplicità rende anche snapshot e rollback più preziosi. Piccoli cambiamenti ovvi sono più facili da confrontare, mantenere o ripristinare.
Quando il codice è facile da produrre, la competenza scarsa è scegliere cosa debba esistere e assicurarsi che sia corretto. Il vecchio consiglio “assumi grandi programmatori” vale ancora, ma il ruolo cambia. Non assumi qualcuno per digitare più velocemente. Assumi qualcuno che sappia giudicare, raffinare e difendere il prodotto.
Le persone più preziose nello sviluppo assistito dall'IA tendono ad avere quattro qualità: giudizio (cosa conta), gusto (cos'è buono), abilità di debug (trovare la causa reale) e comunicazione (rendere chiare le priorità). Sono in grado di prendere una feature scritta dall'AI che “funziona quasi” e trasformarla in qualcosa di cui ti puoi fidare.
Invece di chiedere una soluzione perfetta da zero, dai ai candidati una pull request generata dall'AI (o una diff incollata) con alcuni problemi realistici: nomi poco chiari, un edge case nascosto, test mancanti e un piccolo errore di sicurezza.
Chiedi loro di spiegare in parole semplici cosa prova a fare il codice, trovare le parti a rischio maggiore, proporre correzioni e aggiungere (o abbozzare) test che catturerebbero regressioni. Se vuoi un segnale forte, chiedi anche come cambierebbero le istruzioni perché il prossimo tentativo dell'AI sia migliore.
Questo rivela come pensano in condizioni reali: codice imperfetto, tempo limitato e la necessità di scegliere priorità.
L'IA spesso suona sicura. I buoni assunti sono a loro agio nel contraddire. Sanno dire no a una feature che aggiunge complessità, no a un cambiamento che indebolisce la sicurezza e no a spedire senza prove.
Un segnale concreto è come rispondono a “Lo faresti mergeare?”. I candidati forti non rispondono con sensazioni. Danno una decisione e una breve lista di cambi richiesti.
Esempio: chiedi un aggiornamento rapido di controllo accessi e l'IA suggerisce di spargere controlli in tutti gli handler. Un candidato forte rifiuta quell'approccio e propone un singolo layer di autorizzazione chiaro, più test per i percorsi admin e non-admin.
Infine, costruisci standard condivisi così il team modifica l'output dell'AI allo stesso modo. Fallo semplice: una definizione di fatto completato, aspettative di review coerenti e una baseline di test.
Quando l'IA può generare molto codice in pochi minuti, è allettante saltare il pensiero e iterare. Funziona per demo. Fallisce quando hai bisogno di correttezza, comportamento prevedibile e meno sorprese.
Un buon prompt è spesso una breve specifica travestita. Prima di chiedere codice, trasforma l'obiettivo vago in alcuni criteri di accettazione e non-obiettivi espliciti. Questo evita che l'IA (e il team) espandano silenziosamente lo scope.
Mantieni la specifica piccola ma precisa. Non stai scrivendo un romanzo. Stai mettendo confini su:
Definisci “fatto” prima della generazione, non dopo. “Fatto” dovrebbe essere più di “compila” o “l'interfaccia sembra giusta”. Includi aspettative di test, compatibilità retroattiva e cosa monitorare dopo il rilascio.
Esempio: vuoi “aggiungere reset password”. Una specifica più chiara potrebbe dire: gli utenti richiedono il reset via email; i link scadono in 15 minuti; lo stesso messaggio appare indipendentemente dall'esistenza dell'email; rate limit per IP; registra i tentativi senza memorizzare i token in chiaro. Non-obiettivo: nessuna riprogettazione della pagina di login. Ora il tuo prompt ha dei paletti e le review diventano più semplici.
Tieni un changelog leggero delle decisioni. Un paragrafo per decisione è sufficiente. Nota perché hai scelto un approccio e perché hai scartato alternative. Quando qualcuno chiede “perché è così?” due settimane dopo, avrai una risposta.
Il cambiamento più grande con l'IA è che produrre codice è facile. La parte difficile è decidere cosa il codice deve fare e dimostrare che lo fa.
Inizia scrivendo l'obiettivo e i vincoli in linguaggio semplice. Includi cosa non deve mai succedere, cosa può essere lento e cosa è fuori scope. Un buon vincolo è testabile: “Nessun utente deve vedere i dati di un altro utente” o “I totali devono coincidere con l'export finanziario al centesimo”.
Prima di chiedere codice, chiedi un design semplice e i compromessi. Vuoi che l'AI mostri il suo ragionamento in una forma che tu possa giudicare: cosa memorizzerà, cosa convaliderà e cosa registrerà. Se propone qualcosa di ingegnoso, contrasta e richiedi la versione più semplice che soddisfi ancora i vincoli.
Un loop ripetibile è:
Ecco uno scenario piccolo: aggiungi lo “stato rimborso” a una schermata ordine. L'IA può generare l'interfaccia rapidamente, ma la correttezza vive nei casi limite. Che succede se un rimborso è parziale? Se il provider di pagamenti ritenta un webhook? Scrivi quei casi prima, poi implementa una fetta (colonna DB + validazione) e verifica con test prima di procedere.
Se usi Koder.ai, funzionalità come planning mode, snapshot e rollback si integrano naturalmente in questo loop: pianifica prima, genera a fette e cattura un punto di ripristino sicuro per ogni modifica significativa.
Quando la generazione di codice è veloce, è facile trattare il codice come il prodotto del lavoro. Non lo è. Il prodotto del lavoro è il comportamento: l'app fa la cosa giusta, anche quando le cose vanno storte.
L'IA spesso suona sicura anche quando indovina. L'errore è saltare la parte noiosa: eseguire test, controllare i casi limite e validare input reali.
Una semplice abitudine aiuta: prima di accettare una modifica, chiediti “Come sappiamo che questo è corretto?”. Se la risposta è “sembra giusto”, stai giocando d'azzardo.
L'IA ama aggiungere extra: caching, retry, più impostazioni, più endpoint, una UI più bella. Alcune idee sono utili, ma aumentano il rischio. Molti bug nascono da feature “belle da avere” che nessuno aveva chiesto.
Mantieni un confine netto: risolvi il problema che ti sei posto e poi fermati. Se un suggerimento è valido, catturalo come task separato con i suoi test.
Un grande commit generato dall'AI può nascondere una dozzina di decisioni non correlate. La review diventa una formalità perché nessuno può tenere tutto a mente.
Tratta l'output della chat come una bozza. Spezzalo in piccole modifiche che puoi leggere, eseguire e ripristinare. Snapshot e rollback sono utili solo se li prendi in punti sensati.
Alcuni limiti semplici prevengono la maggior parte dei problemi: una feature per change set, una migrazione per change set, una sola area ad alto rischio alla volta (auth, pagamenti, cancellazione dati), test aggiornati nello stesso change e una nota chiara su come verificare.
L'AI può riprodurre pattern dal training o suggerire dipendenze che non conosci. Anche quando la licenza va bene, il rischio maggiore è la sicurezza: segreti hard-coded, gestione debole dei token o operazioni file/query insicure.
Se non sai spiegare cosa fa uno snippet, non spedirlo. Chiedi una versione più semplice o riscrivila tu.
Molti bug “funziona sulla mia macchina” sono problemi di dati e scala. L'IA può creare cambi di schema senza pensare alle righe esistenti, tabelle grandi o downtime.
Un esempio realistico: il modello aggiunge una nuova colonna NOT NULL a una tabella PostgreSQL e la backfilla con un loop lento. In produzione quello può bloccare la tabella e rompere l'app. Considera sempre cosa succede con un milione di righe, una rete lenta o un deploy fallito a metà.
Immagina un piccolo tracker di richieste interne: le persone inviano richieste, i manager approvano o rifiutano e la finanza marca gli elementi come pagati. Sembra semplice e con l'aiuto dell'IA puoi generare schermate ed endpoint rapidamente. La parte che ti rallenta è la stessa verità di sempre: le regole, non la digitazione.
Inizia scrivendo il minimo che deve essere corretto. Se non riesci a spiegarlo in parole semplici, non puoi testarlo.
Una definizione iniziale stringata spesso appare così: campi (titolo, richiedente, dipartimento, importo, motivo, stato, timestamp); ruoli (richiedente, approvatore, finanza, admin); stati (bozza, inviato, approvato, rifiutato, pagato). Poi indica le transizioni importanti: solo un approvatore può spostare da inviato ad approvato o rifiutato; solo finanza può spostare da approvato a pagato.
Usa l'IA in un ordine controllato così puoi catturare gli errori presto:
I test di maggior valore non sono “la pagina si carica”. Sono i controlli di permesso e le transizioni di stato. Dimostra, per esempio, che un richiedente non può approvare la propria richiesta, un approvatore non può segnare qualcosa come pagato, le richieste rifiutate non possono essere pagate e (se è la tua regola) gli importi non possono essere modificati dopo l'invio.
Ciò che richiede più tempo è chiarire i casi limite. Un approvatore può cambiare idea dopo aver rifiutato? Due approvatori premendo approve contemporaneamente? La finanza deve poter pagare parzialmente? L'IA può generare codice per qualsiasi risposta scegli, ma non può scegliere per te. La correttezza viene dal prendere quelle decisioni e poi costringere il codice a rispettarle.
L'IA può produrre molto codice velocemente, ma l'ultimo miglio è ancora lavoro umano: dimostrare che fa ciò che intendevi e fallire in sicurezza quando non lo fa.
Prima di segnare qualcosa come fatto, scegli la definizione di “fatto” più piccola che conta. Per una piccola feature può essere un happy path, due path di errore e una rapida passata di leggibilità. Per pagamenti o auth alza la soglia.
Diciamo che l'IA aggiunge “inviti bulk” a una schermata admin. L'happy path funziona, ma il rischio reale sono i casi limite: email duplicate, failure parziali e rate limit. Una decisione di spedizione solida potrebbe essere un test automatizzato per i duplicati, un controllo manuale per i messaggi di failure parziale e un piano di rollback.
Quando il codice è economico, il rischio si sposta sulla qualità delle decisioni: cosa hai chiesto, cosa hai accettato e cosa hai spedito. Il modo più rapido per far fruttare queste verità nello sviluppo assistito dall'IA è aggiungere guardrail che impediscano a modifiche “quasi giuste” di passare.
Comincia con una specifica di una pagina per la prossima feature. Mantienila semplice: per chi è, cosa deve fare, cosa non deve fare e qualche test di accettazione scritto in linguaggio quotidiano. Quei test diventano il tuo ancora quando l'IA suggerisce una scorciatoia tentatrice.
Un set di guardrail che scala senza troppo overhead di processo:
I prompt fanno ora parte del processo. Concorda uno stile: quali librerie sono permesse, come si gestiscono gli errori, cosa significa “fatto” e quali test devono passare. Se un prompt non è riutilizzabile da un altro collega, probabilmente è troppo vago.
Se preferisci un modo chat-first per costruire web, backend e app mobile, Koder.ai (koder.ai) è un esempio di piattaforma vibe-coding dove planning mode, snapshot ed export del codice possono supportare questi guardrail. Lo strumento può accelerare le bozze, ma la disciplina è ciò che mantiene gli umani responsabili della correttezza.
Tratta l'output dell'AI come una bozza veloce, non come una feature finita. Inizia scrivendo 3–5 criteri di accettazione pass/fail, poi genera una singola porzione (un endpoint, una schermata, una migrazione) e verificane il funzionamento con test e prove dei casi di errore prima di procedere.
Perché i test sono il luogo in cui scopri cosa il codice fa davvero. L'AI può produrre logiche plausibili che comunque saltano una regola chiave (permessi, retry, stati limite). I test trasformano le tue aspettative in controlli eseguibili, ripetibili e affidabili.
Inizia da ciò che causerebbe il danno maggiore:
Aggiungi copertura dopo aver messo in sicurezza i comportamenti ad alto impatto.
Chiedi l'approccio più semplice con vincoli espliciti, poi elimina i livelli aggiunti a meno che non giustifichino il costo. Una buona regola: non introdurre una nuova astrazione a meno che non rimuova duplicazione in 3+ punti o renda la correttezza più facile da dimostrare.
Scrivi una breve specifica: input, output, errori, vincoli e non-obiettivi. Includi esempi concreti (richieste/risposte di esempio, edge case). Poi definisci “fatto” in anticipo: test richiesti, aspettative di compatibilità e una veloce nota su come verificare.
Spezzala. Mantieni ogni change set recensibile in pochi minuti:
Così la review diventa reale invece di essere un timbro automatico.
Non fidarti della sicurezza apparente: fidati della prova. Esegui test, prova input malformati e verifica i confini dei permessi. Controlla anche per insidie comuni dell'AI: mancanza di controlli auth, costruzione di query insicura, gestione debole dei token e swallowing silenzioso degli errori.
Preferisci endpoint di transizione espliciti invece di un generico “aggiorna qualsiasi cosa”. Per esempio: submit, approve, reject, pay invece di una rotta update generica. Poi scrivi test che impongano chi può eseguire ogni transizione e quali transizioni sono proibite.
Dai ai candidati una diff generata dall'AI con problemi realistici: nomi poco chiari, un test mancante, un edge case e un piccolo problema di sicurezza. Chiedi loro di spiegare l'intento, trovare le parti più a rischio, proporre fix e delineare i test da aggiungere.
Usa le funzionalità dello strumento per supportare un loop disciplinato: pianifica prima, genera a piccoli incrementi, crea snapshot prima delle modifiche rischiose e ripristina se la validazione fallisce. In una piattaforma chat-first come Koder.ai, questo si integra bene con planning mode, snapshot e rollback — specialmente quando tocchi auth, pagamenti o migrazioni.