Andrea Possidente
iten

AI Pubblicato il 11 min di lettura

Vibe coding: perché conoscere il codice conta più di prima

Descrivere un’app in una frase e vederla funzionare in pochi minuti è reale. Ma chi decide la direzione, e chi verifica che il risultato sia corretto, se nessuno sa leggere il codice?

In questo articolo

Oggi basta una frase per ottenere un’applicazione che parte. Scrivi «fammi una dashboard con login, grafici e una tabella filtrabile» e in pochi minuti hai un progetto che compila, si avvia e ha perfino un aspetto decente. Qualche anno fa sarebbe servita una settimana. È un cambiamento enorme e non ha senso fingere il contrario: gli assistenti AI per il codice sono tra gli strumenti più potenti che uno sviluppatore abbia mai avuto a disposizione.

Proprio per questo vale la pena farsi una domanda scomoda. Se generare codice è diventato facile, cosa è diventato difficile? La mia risposta è che la difficoltà non è sparita: si è spostata. Da scrivere il codice a decidere cosa scrivere e a capire se ciò che è stato scritto è giusto. E per queste due cose, conoscere il codice non è meno importante di prima. Lo è di più.

Cosa intendiamo per vibe coding#

L’espressione l’ha resa popolare Andrej Karpathy all’inizio del 2025, descrivendo un modo di programmare in cui ci si abbandona al flusso: si chiede, si accetta, si incolla l’errore quando qualcosa non va, e si va avanti senza leggere davvero il codice. Lui stesso lo presentava come un approccio adatto ai progetti del fine settimana, non come un metodo di lavoro universale.

E in quel perimetro funziona benissimo. Un prototipo da mostrare domani, uno script che userai una volta sola, un esperimento per capire se un’idea regge: sono casi in cui la velocità conta più della solidità, e in cui un errore costa poco. Anche per imparare può essere prezioso, se lo si usa per esplorare invece che per saltare i passaggi.

Il problema nasce quando lo stesso approccio viene portato su ciò che deve durare: un prodotto con utenti veri, dati veri, soldi veri. Lì «funziona sul mio computer» non è più una garanzia di niente.

Il codice plausibile è il più pericoloso#

Un modello linguistico è bravissimo a produrre codice plausibile: ben formattato, con nomi sensati, strutturato come lo scriverebbe una persona competente. Nella stragrande maggioranza dei casi è anche corretto. Ma quando non lo è, l’errore ha lo stesso aspetto del codice giusto. Ed è esattamente questo che lo rende difficile da vedere per chi non sa cosa cercare.

Prendiamo un componente React che mostra i risultati di una ricerca mentre l’utente scrive. È il genere di cosa che un assistente produce in un attimo:

function ProductSearch() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<Product[]>([]);

  useEffect(() => {
    fetch(`/api/products?q=${query}`)
      .then((res) => res.json())
      .then(setResults);
  }, [query]);

  return (/* input e lista dei risultati */);
}

Lo provi, digiti «scarpe», compaiono le scarpe. Funziona. Eppure contiene almeno quattro problemi, nessuno dei quali si vede in una prova veloce:

  • Le risposte possono arrivare in disordine. Se l’utente scrive «sc» e poi «scarpe», la richiesta per «sc» può tornare per ultima e sovrascrivere i risultati giusti con quelli sbagliati. Su una connessione lenta succede spesso.
  • Parte una richiesta a ogni tasto, anche per stringhe di una lettera, e anche quando il campo è vuoto.
  • Il testo non viene codificato: una ricerca che contiene & o # produce un URL diverso da quello previsto.
  • Gli errori non vengono gestiti: se il server risponde con un errore, l’interfaccia resta semplicemente muta.

Una versione più solida non è molto più lunga, ma richiede di sapere che quei problemi esistono:

useEffect(() => {
  if (!query.trim()) {
    setResults([]);
    return;
  }
  const controller = new AbortController();
  const timer = setTimeout(async () => {
    try {
      const res = await fetch(`/api/products?q=${encodeURIComponent(query)}`, {
        signal: controller.signal,
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      setResults(await res.json());
    } catch (error) {
      if (!controller.signal.aborted) setError(error);
    }
  }, 250);
  return () => {
    clearTimeout(timer);
    controller.abort();
  };
}, [query]);

L’assistente sa scrivere anche questa versione, e spesso lo fa se glielo chiedi. Il punto è proprio questo: se glielo chiedi. Per chiederlo devi sapere che la prima versione è fragile.

Chi dà la direzione#

Un prompt è una specifica. E una specifica vaga produce un risultato che riempie i vuoti con le scelte più probabili, non con quelle giuste per il tuo caso.

«Fammi una lista di prodotti» è una richiesta. «Fammi una lista di prodotti paginata lato server, con i filtri nell’URL così che si possano condividere, uno stato di caricamento che non faccia saltare il layout, e che funzioni anche da tastiera» è un’altra richiesta, e produce un altro software. Tra le due non c’è una differenza di abilità nello scrivere prompt: c’è una differenza di conoscenza. Per formulare la seconda devi sapere cos’è la paginazione lato server, perché i filtri nell’URL sono utili, cos’è il layout shift, cosa significa accessibilità da tastiera.

Lo stesso vale per le scelte che contano davvero, quelle di architettura. Rendering lato server o lato client? Dove vive lo stato? Cosa succede quando i dati sono diecimila invece di dieci? Quale libreria aggiungere, e soprattutto quale non aggiungere? Un assistente ti proporrà una risposta ragionevole a ognuna di queste domande. Ma «ragionevole in media» non significa «adatta al tuo progetto», e scegliere tra alternative richiede di capirle.

Nel mio lavoro sul frontend di piattaforme di gioco online, e prima sugli e-commerce che ho costruito e gestito, le decisioni che hanno pesato di più non sono mai state quelle su come scrivere una funzione. Sono state quelle su cosa costruire, in che ordine, con quali compromessi. È esattamente la parte che nessuno strumento può fare al posto tuo, perché dipende da un contesto che solo tu conosci.

Verificare: la competenza che non si può delegare#

Se dare la direzione è la prima metà del lavoro, verificare è la seconda. Ed è quella in cui il vibe coding mostra di più i suoi limiti.

Ecco un endpoint Express che restituisce gli ordini filtrati per stato. Anche questo è il tipo di codice che si ottiene in pochi secondi e che, provato una volta, sembra perfetto:

app.get("/api/orders", async (req, res) => {
  const { status } = req.query;
  const result = await db.query(
    `SELECT * FROM orders WHERE status = '${status}'`,
  );
  res.json(result.rows);
});

Chi conosce un minimo di sicurezza lo vede subito: il valore arriva dall’URL e finisce dentro la query SQL così com’è. È una SQL injection da manuale, e basta un parametro costruito ad arte per leggere o modificare dati che non dovrebbero essere accessibili. Ma non è l’unico problema. Manca qualsiasi controllo su chi sta chiedendo, quindi chiunque vede gli ordini di tutti; SELECT * restituisce anche colonne che forse non dovrebbero uscire dal database; e non c’è alcun limite al numero di righe.

app.get("/api/orders", requireAuth, async (req, res) => {
  const status = String(req.query.status ?? "open");
  const { rows } = await db.query(
    `SELECT id, status, total, created_at
       FROM orders
      WHERE customer_id = $1 AND status = $2
      ORDER BY created_at DESC
      LIMIT 50`,
    [req.user.id, status],
  );
  res.json(rows);
});

Qualcuno potrebbe obiettare che basta chiedere all’AI di scrivere anche i test. Ed è vero che li scrive, spesso bene. Ma i test verificano le ipotesi di chi li scrive. Se il modello non ha considerato la sicurezza nel codice, è improbabile che la consideri nei test: controllerà che lo stato «open» restituisca gli ordini aperti, e il test passerà. Una suite verde dice che il codice fa ciò che il suo autore pensava. Non dice che quello che pensava fosse giusto.

La verifica, quindi, non è un passaggio meccanico da aggiungere alla fine. È un modo di leggere il codice chiedendosi cosa succede quando le cose vanno storte: input inattesi, utenti malintenzionati, reti lente, dati che crescono. Sono domande che nascono dall’esperienza e dalla conoscenza di come funzionano le cose sotto la superficie.

Il debito che non si vede#

C’è poi un costo che si manifesta più tardi: la manutenzione.

Il codice passa molto più tempo a essere letto e modificato che a essere scritto. Un progetto costruito accettando proposte senza capirle funziona finché non serve cambiarlo. Poi arriva il giorno in cui c’è un bug in produzione, o una funzionalità nuova tocca tre punti diversi, e ci si trova davanti a migliaia di righe che nessuno ha mai davvero letto. A quel punto si può chiedere di nuovo all’AI, certo. Ma senza capire il sistema si finisce a sovrapporre correzioni a correzioni, e ogni modifica diventa un tentativo.

Lo stesso vale per i problemi silenziosi. Un div con un gestore di clic al posto di un vero button funziona benissimo con il mouse, ed è invisibile finché qualcuno non prova a usare il sito con la tastiera o con uno screen reader. Un’immagine enorme caricata senza dimensioni rallenta la pagina e peggiora il posizionamento, ma non genera nessun errore. Sono difetti che non fanno rumore, e che si notano solo se si sa che esistono.

Cosa significa oggi «conoscere il codice»#

Detto questo, sarebbe sbagliato concludere che nulla è cambiato. Qualcosa è cambiato davvero, e riguarda quale conoscenza conta.

Pesa meno ricordare a memoria la firma di una funzione o la sintassi esatta di un’API: sono cose che si trovano, e che un assistente ti ricorda in un secondo. Pesa molto di più tutto il resto:

  • Saper leggere il codice con attenzione, anche quello che non hai scritto tu, e capire cosa fa davvero rispetto a cosa sembra fare.
  • Avere modelli mentali solidi di come funzionano le cose: il ciclo di una richiesta HTTP, il rendering di una pagina, lo stato di un’applicazione, un database, un indice, una transazione.
  • Conoscere i fondamentali della sicurezza e dell’accessibilità, perché sono le aree in cui gli errori non si vedono e costano di più.
  • Saper fare debug: formulare un’ipotesi, isolare il problema, verificarla. È la competenza che l’AI sostituisce peggio, perché richiede di ragionare sul tuo sistema specifico.
  • Avere gusto: riconoscere una soluzione troppo complessa, una dipendenza inutile, un’astrazione prematura.

In altre parole, conoscere il codice oggi assomiglia più al lavoro di chi revisiona e progetta che a quello di chi scrive riga per riga. Ed è un lavoro che richiede di aver scritto molte righe prima.

Come uso l’AI, senza farmi usare#

Nel lavoro di tutti i giorni uso assistenti come Claude Code, e li considero un moltiplicatore enorme. Ma con alcune regole che mi sono dato:

  1. Decido io la direzione prima di chiedere. Architettura, vincoli e compromessi li scelgo io; all’AI chiedo di eseguire una decisione, non di prenderla al mio posto.
  2. Procedo a piccoli passi. Una modifica circoscritta si legge e si capisce; un’intera funzionalità generata in un colpo solo no.
  3. Leggo ogni riga che finisce nel progetto. Se non riesco a spiegare cosa fa un pezzo di codice, non lo accetto.
  4. Chiedo spiegazioni e alternative. «Perché hai scelto questo approccio?» e «Quali sono le alternative e i loro svantaggi?» sono tra le domande più utili che si possano fare.
  5. Scrivo io i test che contano, almeno quelli sui casi limite e sui comportamenti che non devono mai rompersi.
  6. Lo uso per imparare. Quando propone qualcosa che non conosco, è l’occasione per capirlo, non per copiarlo.

Il risultato è che vado più veloce di prima, ma resto io il responsabile di ciò che viene pubblicato. Ed è giusto così: il codice lo firma chi lo mette in produzione, non lo strumento che lo ha suggerito.

Per chi sta iniziando#

Il rischio più grande, secondo me, riguarda chi sta imparando adesso. La tentazione di saltare la parte faticosa è fortissima, perché l’AI produce subito qualcosa che funziona. Ma è proprio la parte faticosa a costruire la capacità che poi serve per guidare e controllare questi strumenti.

Quando ho iniziato, tra corsi, certificazioni e un bootcamp, la parte che mi ha insegnato di più non è stata quella in cui le cose funzionavano al primo colpo. È stata quella in cui si rompevano e dovevo capire perché. Se oggi dovessi ricominciare, userei l’AI come un tutor paziente, a cui chiedere spiegazioni, non come qualcuno che fa i compiti al posto mio. Scriverei a mano i primi progetti, anche se più lentamente. E cercherei di capire bene le basi: il linguaggio, il web, i dati. Sono le cose che non invecchiano quando cambia lo strumento di moda.

Il pilota automatico#

Gli aerei di linea hanno sistemi automatici capaci di gestire gran parte di un volo. Eppure i piloti continuano ad addestrarsi a lungo, e non è per nostalgia: è perché il loro valore emerge proprio nei momenti in cui l’automatismo non basta, e in quei momenti devono sapere esattamente cosa sta succedendo.

Con il codice vale lo stesso. Gli strumenti AI hanno cambiato il modo in cui lavoriamo, e in meglio. Ma non hanno cambiato chi è responsabile del risultato. Più il codice diventa facile da produrre, più diventa prezioso chi sa dire se è quello giusto. Conoscere il codice non è un’abilità che l’AI ha reso superflua: è quella che permette di usarla davvero.

Full Stack Web Developer. Scrivo di quello che imparo costruendo interfacce e applicazioni web.

Commenti Nessun commento

Ancora nessun commento: puoi essere il primo.

Lascia un commento

I commenti vengono pubblicati dopo la moderazione.