modulo 07 / lezione 45
Leggere e verificare codice generato
Domande da fare all'AI sul proprio output: spiega, motiva, semplifica
Usare l'AI come revisore del suo stesso codice, mantenendo però la verifica umana sul risultato.
Domande da fare all’AI sul proprio output: spiega, motiva, semplifica
L’AI è un buon revisore di codice altrui. È un buon revisore anche del proprio codice, se gli fai le domande giuste. La trappola: il modello tende a difendere ciò che ha appena prodotto se chiedi “va bene?”. Tre tipi di domande — spiega, motiva, semplifica — costringono a un esame più rigoroso.
Perché questa lezione conta
Dopo che il modello ha generato codice, hai un’opportunità preziosa: nello stesso contesto, con tutto in mente, può rivedere ciò che ha appena fatto. Domande generiche (“è ok?”) raccolgono difese generiche (“sì, sembra buono”). Domande mirate (spiega, motiva, semplifica) producono insight reali, identificano debolezze, suggeriscono alternative.
Obiettivo operativo
Alla fine devi saper:
- usare le tre famiglie di domande in modo strategico;
- distinguere risposta utile da auto-giustificazione;
- concludere ogni generazione significativa con review domandata.
Nucleo della lezione
1. Spiega: cosa fa il codice
Domande “spiega” forzano il modello a ricostruire il flusso che ha generato. Spesso l’esercizio rivela problemi nascosti.
Domande utili:
- “Spiega in 3 frasi cosa fa questa funzione, senza usare i nomi delle variabili.”
- “Quali sono i 3-4 invarianti che questo codice mantiene?”
- “Se guardo solo i tipi delle funzioni esportate, cosa capisco di cosa fa il modulo?”
- “Disegna un mini-diagramma testuale del flusso dei dati attraverso queste funzioni.”
Cosa cerchi: spiegazione coerente, senza salti, senza “magic” non spiegato. Se la spiegazione è confusa, il codice è confuso.
Risposta utile: descrizione chiara con riferimento a 2-3 punti specifici del codice. Risposta inutile: parafrasi generica (“questa funzione gestisce…“).
2. Motiva: perché così
Domande “motiva” forzano a render visibili scelte tecniche.
Domande utili:
- “Perché questa libreria invece di alternative? Quali alternative hai considerato?”
- “Perché questa struttura di file e non altre?”
- “Perché questo pattern di gestione errori (Result vs throw, ecc.)?”
- “Cosa giustifica questo livello di astrazione? Sarebbe più semplice qualcosa di più diretto?”
- “Quali assunzioni hai fatto che non sono visibili nel codice?”
Cosa cerchi: motivazioni con trade-off, non difese cieche. “Ho usato X perché…”, non “X è il più popolare”.
Risposta utile: cita criteri concreti (performance, leggibilità, vincoli del progetto) e ammette quando è preferenza. Risposta inutile: tautologia (“uso X perché funziona”).
3. Semplifica: cosa togliere
Domande “semplifica” sono le più produttive. Codice è quasi sempre più complesso del necessario.
Domande utili:
- “Quali parti di questo codice sono speculative (per casi che non sono nei requisiti)?”
- “Cosa potresti rimuovere senza rompere il comportamento richiesto?”
- “Se dovessi riscriverlo in metà delle righe, cosa salveresti?”
- “Quali astrazioni potrebbero essere inlinate senza perdere qualcosa?”
- “Quali funzioni potrebbero essere fuse senza perdere chiarezza?”
- “Quali condizioni potrebbero essere rese più dirette?”
Cosa cerchi: parti che sono lì per “abitudine”, non per necessità. Astrazioni gratuite. Difensività eccessiva (try/catch ovunque). Wrapper che non aggiungono.
Risposta utile: lista di 3-5 cose specifiche da rimuovere/inlineare con motivo. Risposta inutile: “il codice è già abbastanza semplice”.
4. Domande aggiuntive: rischi e edge case
Oltre alle tre famiglie principali:
- “Quali edge case NON ho considerato in questo codice?”
- “Se devo testare questa funzione, quali casi ti vengono in mente?”
- “Quale parte di questo codice è più probabile che si rompa fra 6 mesi?”
- “Se un altro sviluppatore lo legge, quale parte capirebbe meno?”
- “Quale parte ti fida meno tu stesso?”
Le ultime due sono insolite ma molto utili. Forzano onestà su zone d’ombra.
Procedura pratica
Workflow di review con AI sul suo output:
- Codice generato e committato (stato pulito).
- Pausa: prima della prossima generazione, fai review.
- 3-5 domande mirate delle famiglie sopra. Una alla volta, non tutte insieme.
- Valuta risposte: utili o auto-giustificative?
- Applica suggerimenti utili: con giudizio. Non tutto va applicato.
- Salta autogiustificazioni: chiedi conferma con altra domanda.
- Commit di refactor: se applichi modifiche, commit separato dal codice originale.
Tempo: 5-10 minuti. Non sempre necessario, ma per parti critiche, sempre.
Laboratorio guidato
Codice generato (esempio):
class UserManager {
private users: Map<string, User> = new Map()
async addUser(user: User): Promise<void> {
if (!user.email || !user.name) throw new Error('Invalid')
if (this.users.has(user.id)) throw new Error('Exists')
this.users.set(user.id, user)
await this.notifySubscribers('add', user)
}
async notifySubscribers(event: string, data: any): Promise<void> {
for (const sub of this.subscribers) {
try { await sub(event, data) } catch (e) { console.log(e) }
}
}
subscribers: Function[] = []
subscribe(fn: Function): () => void {
this.subscribers.push(fn)
return () => { this.subscribers = this.subscribers.filter(f => f !== fn) }
}
}
Domande di review:
Spiega: “Senza usare nomi di variabili, cosa fa questa classe? Quali responsabilità ha?”
Risposta tipo: “Mantiene una collezione di oggetti, gestisce eventi di aggiunta, notifica osservatori esterni in modo asincrono ignorando errori”.
Già dal “ignorando errori” emerge problema.
Motiva: “Perché Map invece di plain object? Perché classe invece di funzioni? Perché Function come tipo subscribers?”
Risposta tipo: “Map per chiavi tipizzate; classe per stato condiviso; Function per generalità”.
Il “Function” generico è bandiera: meglio tipo specifico.
Semplifica: “Cosa è speculativo? Cosa potresti rimuovere?”
Risposta tipo: “il try/catch silenzioso catch (e) { console.log(e) } nasconde errori. Subscribers come Function perde tipi. La classe ha senso solo se uso istanze multiple; se è singleton, modulo con funzioni è più semplice”.
Tre punti azionabili. Applichi 2-3, codice migliora.
Output atteso
Una libreria personale di prompt di review post-generazione, per famiglia (spiega, motiva, semplifica) + edge case + rischi. Salvati come template, copiabili dopo ogni generazione.
Studio passo passo
Studia questa lezione in tre passaggi, senza correre subito al prompt o al codice.
- Prima lettura: chiarisci l’obiettivo: usare l’AI come revisore del suo stesso codice, mantenendo però la verifica umana sul risultato. Se non riesci a riscriverlo in una frase tua, rileggi il nucleo della lezione.
- Seconda lettura: trasforma i contenuti in domande operative:
- Chiedere spiegazioni strutturali: quali file, quali responsabilità, quale flusso, quali invarianti.
- Chiedere motivazioni: perché questa dipendenza, perché questa astrazione, perché questo stato, perché questo test.
- Chiedere semplificazione: rimuovere parti speculative, ridurre scope, rinominare, isolare responsabilità.
- Applicazione: svolgi il laboratorio: far spiegare a un modello un diff generato e confrontare la spiegazione con il codice reale. L’output da ottenere è: una lista di domande di review da usare dopo ogni generazione significativa.
Prima di passare alla lezione successiva, controlla di avere un artefatto concreto: una tabella, un prompt, una checklist, un mini report, un diff commentato o una decisione scritta. Nel vibe coding la comprensione resta astratta finché non produce una traccia verificabile.
Checklist di verifica
- conduco review post-generazione su codice critico;
- so distinguere risposta utile da auto-difesa;
- ho applicato almeno una semplificazione suggerita dall’AI sul suo output;
- chiedo “cosa NON hai considerato” non solo “va bene?”.
Errori comuni
Domande generiche. “Va bene?” → “Sì”. Niente nuovo.
Tutte domande in un colpo. Risposta diventa lista di paragrafi. Una alla volta produce profondità.
Accettare ogni semplificazione. L’AI può togliere troppo. Giudica.
Skippare review. “L’AI ha già finito”. Review è dove emergono problemi sottili.
Non documentare scoperte. Una review utile rivela pattern. Annotali in decisions.md o regole.
Saltare a “riscrivi tutto”. Se review rivela problemi grossi, valuta refactor; non sempre serve riscrivere.
Prompt di lavoro
Spiega:
Spiega in 3 frasi cosa fa questo codice, senza usare i nomi delle variabili e funzioni.
[INCOLLA CODICE]
Concludi con: quali invarianti il codice mantiene? Cosa NON garantisce?
Motiva:
Per ogni scelta tecnica nel seguente codice, motiva con criteri concreti.
[INCOLLA CODICE]
Cose da motivare:
- librerie/dipendenze scelte
- struttura (classe vs funzioni, file/cartelle)
- pattern di gestione errori
- livello di astrazione
Per ognuna: perché questa, quali alternative hai considerato, trade-off.
Sii onesto: se è preferenza non motivata, dillo.
Semplifica:
Trova 3-5 cose da rimuovere o inlineare senza cambiare comportamento richiesto.
[INCOLLA CODICE]
Per ognuna:
1. Cosa rimuovi/inlinei.
2. Motivo (speculativo / difensività eccessiva / astrazione gratuita / wrapper inutile).
3. Come cambia il codice (mostra la versione semplificata di quel pezzo).
NON riscrivere tutto. Solo le 3-5 modifiche più di valore.
Esercizio finale
Per il prossimo task significativo: dopo che il modello ha generato il codice, prima di fare merge, fai review con almeno una domanda per famiglia (spiega, motiva, semplifica). Annota le scoperte. La maggior parte delle volte, almeno una semplificazione utile emerge. Quel ritmo (genera + review prima di accettare) è la differenza tra codice generato e codice posseduto.