Le verità sul software di Joel Spolsky per lo sviluppo assistito dall'IA
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.

Perché queste verità contano ancora quando l'IA scrive codice velocemente
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é.
I test restano il collo di bottiglia, e questo è un bene
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.
Un esempio rapido
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.
La semplicità batte l'ingegnosità, specialmente con l'IA nel loop
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:
- Un nuovo collega può capire il flusso principale in una sola sessione?
- Potresti cancellare questa astrazione e sostituirla con codice esplicito senza perdere chiarezza?
- C'è un posto ovvio per correggere un bug, o cinque?
- Ogni parte ha un solo compito che puoi esprimere in una frase?
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.
Assunzioni: ti servono editor e decisori, non dattilografi
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.
Un'intervista migliore: migliora una modifica generata dall'AI
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à.
Il superpotere: saper dire “no”
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.
Specifiche e pianificazione: prompt più chiari partono da pensieri più chiari
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:
- Input: cosa arriva (campi, formati, edge case)
- Output: cosa deve uscire (inclusi esempi)
- Errori: cosa può andare storto e come rispondere
- Vincoli: performance, privacy, dipendenze o aree “da non modificare”
- Non-obiettivi: cosa non farà questa modifica
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.
Un workflow pratico assistito dall'IA che puoi ripetere
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 è:
- Scrivi una breve descrizione del problema e 3–5 controlli di accettazione chiari pass/fail.
- Chiedi un piano minimale: modello dei dati, funzioni chiave e cosa potrebbe andare storto.
- Genera una sola piccola modifica alla volta (un endpoint, una schermata, una migrazione), non l'intera app.
- Revisiona come un editor: leggi i diff, esegui i test, prova i casi di errore, poi chiedi correzioni.
- Rilascia in sicurezza: usa un flag o un rollout limitato, osserva log e metriche e sii pronto a tornare indietro.
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.
Errori comuni quando l'IA rende il coding troppo facile
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.
1) Credere all'output sicuro senza provarlo
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.
2) Lasciare che lo strumento espanda lo scope
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.
3) Unire cambi troppo grandi da rivedere
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.
4) Copiare codice con licenze o rischi di sicurezza poco chiari
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.
5) Dimenticare migrazioni, limiti e modalità di fallimento
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à.
Esempio: una piccola app dove la correttezza conta più della velocità
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:
- Definisci prima schema DB ed enum di stato.
- Genera endpoint intorno alle transizioni (submit, approve, reject, pay), non una rotta generica update-anything.
- Genera l'interfaccia per ultima, basandoti su ciò che l'API permette.
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.
Checklist rapida prima di spedire modifiche generate dall'IA
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.
I cinque controlli
- Ogni requisito è verificabile. Per ogni requisito hai un test o una verifica manuale scritta in una frase. Se non sai spiegare come verificarlo, probabilmente non capisci cosa hai costruito.
- Il fallimento è gestito e spiegato. Prova i principali casi d'errore (input errati, permessi mancanti, failure di rete, DB vuoto). Assicurati che l'app mostri un messaggio utile e non trapeli dettagli sensibili.
- Il design è rimasto semplice. Se l'IA ha aggiunto helper, astrazioni o pattern furbi, chiediti: lo terrei se lo avessi scritto a mano? Elimina i layer extra a meno che non paghino l'affitto.
- Un revisore fresco può seguirlo. Presumi che il revisore non abbia visto la sessione di chat. Il codice dovrebbe leggere come una storia: nomi chiari, funzioni brevi e una nota breve sul perché della modifica.
- Puoi annullarlo in sicurezza. Sappi cosa significa “tornare alla normalità”. Cattura una versione nota buona e conferma di poter rollbackare rapidamente se la produzione si comporta diversamente.
Un esempio rapido
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.
Prossimi passi: aggiungi guardrail e poi scala il processo
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:
- Mantieni le modifiche abbastanza piccole da poterle revisionare in pochi minuti, non ore.
- Richiedi test (o almeno note di test) per ogni cambiamento di comportamento.
- Usa un template standard per i prompt: vincoli, stile di codice e aspettative di test.
- Tieni un percorso di rollback affidabile per ogni deploy.
- Traccia gli ignoti come TODO espliciti, non come assunzioni nascoste.
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.
Domande frequenti
Qual è il modo più sicuro di usare l'AI quando può generare codice così in fretta?
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é il testing continua a essere così importante con codice generato dall'AI?
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.
Cosa dovrei testare prima in un progetto assistito dall'AI?
Inizia da ciò che causerebbe il danno maggiore:
- Flussi principali (signup, checkout, salvataggio, esportazione)
- Permessi (chi può vedere/modificare/eliminare)
- Integrità dei dati (no duplicati, totali corretti, migrazioni sicure)
- Modi di fallimento (timeout, retry, stati vuoti, input errati)
Aggiungi copertura dopo aver messo in sicurezza i comportamenti ad alto impatto.
Come faccio a evitare che l'AI generi un'architettura troppo complessa?
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.
Come trasformo una richiesta vaga in un buon prompt?
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.
Come evito commit AI troppo grandi che nessuno riesce a rivedere?
Spezzala. Mantieni ogni change set recensibile in pochi minuti:
- Una feature per change set
- Una migrazione per change set
- Test aggiornati nello stesso change
- Una breve nota: cosa è cambiato, come verificare e cosa potrebbe rompersi
Così la review diventa reale invece di essere un timbro automatico.
Quali sono i rischi principali di accettare l'output dell'AI così com'è?
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.
Come dovrei strutturare API e regole di business per renderle più difficili da sbagliare?
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.
Come posso intervistare ingegneri per lo sviluppo assistito dall'AI?
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.
Come si integrano snapshot e rollback in un workflow assistito dall'AI?
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.