Debugging efficace con console.log e gli strumenti del browser

by Anastasia P.
SHARE
Debugging efficace con console.log e gli strumenti del browser
© Guida-HTML5.it

Introduzione

Quando un programma JavaScript non si comporta come previsto, il primo istinto è spesso quello di aggiungere qualche console.log() qua e là. È un buon punto di partenza, ma il debugging davvero utile nasce dalla combinazione tra console e strumenti del browser, come la scheda Sources, i breakpoint e l’ispezione delle variabili in tempo reale.

In questo tutorial vedremo un approccio pratico per individuare bug in modo ordinato, senza riempire il codice di log temporanei difficili da gestire. L’obiettivo è imparare a capire cosa sta succedendo, dove succede e perché succede, usando gli strumenti già integrati nel browser.

Il sotto-argomento che approfondiamo qui è molto utile nella pratica: debugging con console.log e breakpoint nel browser. È una competenza fondamentale per chi sviluppa applicazioni front-end o lavora con codice asincrono, eventi DOM e chiamate API.

Codice completo

Immaginiamo una piccola pagina con un pulsante che carica dei dati finti e li mostra a schermo. Il bug che vogliamo analizzare è semplice: a volte la lista non viene aggiornata correttamente.

// Simuliamo un piccolo stato applicativo
const appState = {
  loading: false,
  items: [],
  lastFetchAt: null
};

const button = document.querySelector(´#loadBtn´);
const output = document.querySelector(´#output´);

button.addEventListener(´click´, async () => {
  console.log(´Click sul pulsante: avvio caricamento´);

  appState.loading = true;
  render();

  try {
    const data = await fetchFakeItems();
    console.log(´Dati ricevuti dal server simulato:´, data);

    // Possibile punto di bug: i dati potrebbero non essere un array
    if (!Array.isArray(data)) {
      throw new Error(´Il formato dei dati non è valido´);
    }

    appState.items = data;
    appState.lastFetchAt = new Date().toISOString();
  } catch (error) {
    console.log(´Errore durante il caricamento:´, error);
    appState.items = [];
  } finally {
    appState.loading = false;
    render();
    console.log(´Stato finale:´, appState);
  }
});

function render() {
  if (appState.loading) {
    output.textContent = ´Caricamento in corso...´;
    return;
  }

  if (appState.items.length === 0) {
    output.textContent = ´Nessun elemento disponibile´;
    return;
  }

  output.innerHTML = appState.items
    .map(item => `<li>${item.name}</li>`)
    .join(´´);
}

function fetchFakeItems() {
  return new Promise((resolve) => {
    setTimeout(() => {
      const shouldFail = Math.random() > 0.5;

      if (shouldFail) {
        resolve({ message: ´Formato non previsto´ });
      } else {
        resolve([
          { id: 1, name: ´Notebook´ },
          { id: 2, name: ´Mouse´ },
          { id: 3, name: ´Tastiera´ }
        ]);
      }
    }, 800);
  });
}

Questo esempio è utile perché contiene alcuni punti tipici da controllare: un evento click, una funzione asincrona, una risposta non sempre valida e un rendering della UI che dipende dallo stato.

Spiegazione

Il debugging con la console funziona bene quando inserisci messaggi mirati nei punti strategici del flusso. Nel nostro esempio, i log principali sono:

  • prima della richiesta, per verificare che l’evento sia stato davvero attivato;
  • subito dopo la risposta, per controllare la forma dei dati ricevuti;
  • nel blocco catch, per capire se si è verificato un errore;
  • alla fine, per osservare lo stato aggiornato dell’applicazione.

Perché questo approccio è utile? Perché non stai solo “stampando cose”, ma stai tracciando il percorso dei dati. In pratica, stai seguendo il ciclo di vita di una variabile: da quando nasce, a come cambia, fino a quando viene usata nella UI.

Gli strumenti del browser completano il lavoro. Aprendo DevTools e andando nella scheda Sources, puoi:

  • mettere un breakpoint sulla riga che ti interessa;
  • eseguire il codice passo dopo passo con Step over;
  • controllare il valore delle variabili nel pannello Scope;
  • ispezionare lo stack delle chiamate per capire da dove arriva l’esecuzione.

Ad esempio, se il problema è che la lista mostra “Nessun elemento disponibile” anche quando la richiesta sembra andata a buon fine, puoi fermarti proprio dopo const data = await fetchFakeItems(); e verificare il contenuto di data. Nel nostro esempio, potresti scoprire che a volte non è un array ma un oggetto con un messaggio di errore. Questo spiega perché il controllo Array.isArray(data) fallisce e genera un’eccezione.

Un altro vantaggio dei breakpoint rispetto ai log è che non alterano il flusso del codice con troppe istruzioni temporanee. Inoltre, puoi ispezionare anche oggetti complessi, array annidati e variabili locali che non sempre sono facili da leggere in console.

Se vuoi aumentare la precisione, puoi usare anche breakpoint condizionali. Per esempio, puoi fermarti solo quando una certa condizione è vera, come quando data non è un array. Questo è molto utile in applicazioni grandi, dove un bug si verifica solo in casi rari.

Best practice

  • Usa log brevi e significativi: evita messaggi generici come “qui” o “test”. Meglio scrivere cosa stai controllando, ad esempio “Dati ricevuti dal server simulato”.
  • Rimuovi i log temporanei quando hai finito. Lasciare decine di console.log() nel codice rende più difficile la manutenzione.
  • Preferisci breakpoint per il debugging profondo. La console è ottima per osservare, ma il debugger del browser è migliore quando devi analizzare il flusso.
  • Controlla gli stati intermedi: spesso il bug non è nel risultato finale, ma in un passaggio precedente che modifica i dati in modo inatteso.
  • Ispeziona oggetti e array in modo strutturato, non solo come stringhe. La console del browser permette di espandere i valori e vedere dettagli utili.
  • Fai attenzione al codice asincrono: con async/await e promesse, il punto in cui il codice si ferma non è sempre ovvio. I breakpoint aiutano molto a seguire il flusso reale.

Un consiglio pratico: quando il bug è complesso, prova a combinare un console.log() con un breakpoint nello stesso punto. Il log ti dà una traccia rapida, mentre il breakpoint ti permette di fermarti e indagare con calma.

Riepilogo

Il debugging in JavaScript non significa solo stampare messaggi in console. Il vero salto di qualità arriva quando impari a usare in modo coordinato console.log() e gli strumenti di debug del browser.

  • console.log() è utile per tracciare il flusso e verificare i valori.
  • Breakpoint e Sources permettono di fermare l’esecuzione e analizzare il problema in dettaglio.
  • Scope e Call stack ti aiutano a capire lo stato corrente e l’origine dell’esecuzione.
  • Breakpoint condizionali sono ideali quando il bug appare solo in certe condizioni.

Se impari a leggere il comportamento del codice invece di limitarti a “sparare log”, debuggherai più velocemente e con meno frustrazione.

Approfondisci con risorse ufficiali

  • MDN Web Docs - Console: documentazione completa sugli strumenti console disponibili nei browser.
  • MDN Web Docs - Debugger: guida utile per comprendere il debugging nelle DevTools.
  • Chrome DevTools - JavaScript debugging: documentazione ufficiale per usare breakpoint, step-by-step e ispezione variabili.
  • Firefox Developer Tools - Debugger: risorsa ufficiale per il debugger di Firefox.

Studiare questi strumenti con esempi reali ti aiuterà a passare da un debugging “reattivo” a un debugging più metodico e professionale.

SHARE