modulo 03 / lezione 16

Osservare il programma

Breakpoint, logpoint, watch expression

Fermare il programma nel punto giusto con breakpoint condizionati e logpoint, osservare variabili e impostare exception breakpoint senza modificare il codice.

65 min Debugging

In parole semplici

console.log è un coltellino svizzero. Il debugger è un microscopio. Permette di fermare il programma a una riga precisa, ispezionare ogni variabile, eseguire una riga alla volta, scoprire chi sta chiamando chi. E, cosa preziosa, senza modificare il codice.

Questa lezione introduce i tipi di breakpoint che fanno la differenza tra “metto log a caso” e “osservo il programma come uno scienziato”.

Parole nuove

  • breakpoint: punto del codice dove il debugger ferma l’esecuzione.
  • breakpoint condizionato: si attiva solo se una condizione è vera.
  • logpoint: come un breakpoint, ma non ferma — stampa solo un messaggio.
  • watch expression: espressione la cui valutazione viene aggiornata ad ogni step.
  • exception breakpoint: ferma il programma quando viene lanciata un’eccezione.
  • step over / into / out: comandi per avanzare riga per riga.

Cosa impari

  • Usare DevTools (o l’editor) per impostare breakpoint senza modificare i sorgenti.
  • Distinguere breakpoint normali, condizionati, logpoint, exception breakpoint, DOM breakpoint, XHR breakpoint.
  • Avanzare passo per passo (step over, step into, step out) sapendo cosa significano.
  • Osservare lo stato delle variabili con watch e scope panel.

Aprire il debugger

Browser (Chrome / Edge / Firefox / Safari)

DevTools → tab Sources (Chrome) o Debugger (Firefox).

Vai al file. Clicca sul numero di riga: appare un marker blu. Quel punto è ora un breakpoint.

Node.js

node --inspect-brk app.js

Apri Chrome a chrome://inspect, clicca sul target, hai un debugger remoto.

VS Code

Pannello “Run and Debug”. Premi F5. VS Code può attaccarsi a Node, browser o test.

Breakpoint semplice

Cliccando sul numero di riga, il programma si ferma lì. Vedi:

  • Scope: variabili locali, parametri, globali.
  • Call stack: chi ti ha chiamato.
  • Watch: espressioni che ti interessano.

Comandi:

  • Resume (F8): riparti fino al prossimo breakpoint.
  • Step over (F10): esegui la riga e fermati alla successiva (non entrare in funzioni).
  • Step into (F11): entra nella funzione chiamata sulla riga.
  • Step out (Shift+F11): finisci la funzione corrente e fermati a chi l’ha chiamata.

Breakpoint condizionato

A volte vuoi fermarti solo in un caso su mille. Esempio:

“Fermati a riga 42 solo se utente.id === 12345.”

Right-click sul marker → “Edit breakpoint” → inserisci utente.id === 12345. Solo quando la condizione è vera, il programma si ferma. Negli altri 9999 passaggi non noti nulla.

Risparmia ore in cicli e in flussi ad alta frequenza.

Logpoint (il console.log senza toccare il codice)

A volte non vuoi fermarti, vuoi solo loggare. Senza modificare il sorgente.

Right-click sul marker → “Add logpoint” → scrivi "id:", utente.id, "stato:", utente.attivo.

Il debugger stamperà quei valori a ogni passaggio. Quando hai finito, rimuovi il logpoint: il codice non è mai stato toccato.

Vantaggio enorme: niente commit “WIP log”, niente rischio di lasciare log in produzione.

Exception breakpoint

DevTools, pannello Sources, sulla destra: “Pause on exceptions” e “Pause on caught exceptions”.

  • Uncaught only: ferma quando un’eccezione non viene catturata.
  • All exceptions: ferma anche quelle gestite da try/catch.

Utile per scoprire dove l’eccezione nasce prima che venga catturata e magari ignorata da un catch silenzioso.

DOM breakpoint (browser)

Click destro su un nodo in tab Elements → “Break on” →

  • Subtree modifications: ferma quando qualcosa cambia tra i figli.
  • Attribute modifications: ferma quando un attributo cambia.
  • Node removal: ferma quando il nodo viene rimosso.

Utile quando un elemento “sparisce” e non sai chi lo rimuove.

XHR / fetch breakpoint

Sources → “XHR/fetch breakpoints” → aggiungi un pattern URL. Il debugger si ferma prima della chiamata che corrisponde al pattern. Utile per capire chi sta facendo quella richiesta che non dovrebbe partire.

Watch expression

Aggiungi espressioni nel pannello Watch:

utente.nome
carrello.length
JSON.stringify(payload)

Si aggiornano ad ogni step. Più produttive di guardare lo scope intero.

debugger; come breakpoint da codice

function calcola(x) {
  debugger;        // si ferma qui se DevTools è aperto
  return x * 2;
}

Utile quando non puoi raggiungere la riga via DevTools (codice generato, eval, asset minificato).

Esercizio guidato

  1. Apri una piccola funzione che fa un ciclo su 100 elementi.
  2. Metti un breakpoint condizionato: si ferma solo all’iterazione i === 50.
  3. Aggiungi un watch sull’oggetto corrente.
  4. Avanza con step over per due righe, poi step into dentro una sotto-funzione, poi step out.
  5. Attiva “Pause on exceptions” e provoca un errore: osserva la differenza con un’esecuzione senza pausa.
  6. Sostituisci il breakpoint con un logpoint equivalente. Osserva che il programma non si ferma più.

Quando il breakpoint è meglio del log

  • Vuoi guardare molte variabili insieme senza loggarle tutte.
  • Devi capire da chi vieni chiamato (call stack).
  • Vuoi modificare lo stato a runtime per provare cose (poi torni indietro).
  • Il problema è asincrono e devi seguire passo per passo.

Errori frequenti del principiante

  • Usare solo console.log. Per la maggior parte dei bug, breakpoint condizionato risparmia ore.
  • Mettere breakpoint senza watch. Si guarda lo scope e ci si perde tra 50 variabili.
  • Lasciare debugger; in produzione. Va rimosso, oppure controllato da una flag.
  • Avere paura di step into. È l’unico modo per capire cosa fa davvero una funzione che non hai scritto tu.