Debugging with Breakpoints

A debugger pauses your program at a breakpoint, lets you run it one line at a time, and shows every variable's value as you go, in VS Code for Node and in the browser's DevTools for a page, so you can see the line where a value goes wrong.

Apprentice · JavaScript · Software Development · Updated

After this lesson you can

  • Pause a program at a breakpoint and step through it line by line
  • Watch variables in the debugger to find where a value goes wrong

Key takeaways

  • A breakpoint marks a line where the debugger pauses the program. The highlighted line hasn't run yet.
  • Step Over runs one line, Step Into goes inside the function on that line, Step Out finishes the function, and Continue runs to the next breakpoint.
  • The Variables panel shows every variable's value at the pause. Quotes around a value mean it's a string.
  • The Watch panel keeps an eye on any expression you type, such as typeof servings, at every pause.
  • The same tools are in VS Code for Node and in the browser's DevTools (the Sources panel) for a page.

The hardest bugs don't stop your program with an error. It runs, and the answer is wrong: a total that's too big, a name that's missing, a game score that never goes up. You can sprinkle console.log lines everywhere and run it again and again, and that works. But there's a tool made for this job, the debugger, which every professional uses. It pauses your program in the middle, shows you every value, and lets you move forward one line at a time until you see the moment something goes wrong. It's the same tool whether the bug is in a class assignment, a game or an app at work.

A bug you can't see

This script scales the pancake recipe (it serves 4) for a brunch party: 10 guests plus 2 extra helpings. The number of guests comes from a form, so it arrives as text. Copy it into a file named party.js:

// party.js: scale the pancake recipe for a brunch party
const recipe = [
  { name: "flour", cups: 2 },
  { name: "milk", cups: 1.5 },
  { name: "sugar", cups: 0.25 },
];
const recipeServes = 4;

function scaleCups(cups, servings) {
  const factor = servings / recipeServes;
  return cups * factor;
}

const guestsTyped = "10"; // typed into a form, so it's text
const extraHelpings = 2;
const servings = guestsTyped + extraHelpings;

for (const item of recipe) {
  console.log(`${item.name}: ${scaleCups(item.cups, servings)} cups`);
}
flour: 51 cups
milk: 38.25 cups
sugar: 6.375 cups

12 servings should need 6 cups of flour, not 51. Nothing crashed, so there's no error message to read. Somewhere a value is wrong, and you can't see which one by looking at the output.

Breakpoints: pause the program

Think of a sports replay: you pause on the exact moment of the foul and look at where every player is standing. A breakpoint is that pause button for code: a mark on a line that tells the debugger stop here, before you run this line.

To set one in VS Code:

  1. Open your folder in VS Code and open party.js.
  2. Move the mouse to the left of the line number of const factor = servings / recipeServes; (line 10). A faint red dot appears. Click it, and the dot stays: that's your breakpoint.
  3. Start the debugger: Run > Start Debugging, or F5 (on a Mac laptop you may need fn+F5). If VS Code asks which debugger to use, choose Node.js.

The program runs until it reaches line 10, then pauses. The line is highlighted in yellow. The highlighted line hasn't run yet: it's the next one to run. factor doesn't have a value yet.

VS Code paused at a breakpoint on line 10 of party.js: the line is highlighted, and the Variables panel on the left shows servings as "102" in quotes, which reveals it's text

Look at the values

While the program is paused, the Run and Debug view on the left shows what the program knows at this moment:

  • Variables lists every variable that exists here, with its value. Inside scaleCups it shows cups: 2 and servings: "102".
  • Watch shows any expression you add. Click the + at the top of the Watch panel and type typeof servings: it shows "string".
  • Call Stack shows which function called which to get here: scaleCups on top, and below it the line that called it.

Look again at servings. Its value has quotes around it, "102" (VS Code may show single quotes, '102'). Quotes mean it's a string, not a number. "10" + 2 doesn't add: it joins the text "10" and 2 into "102", as you saw in Types and Converting Between Them. Then "102" / 4 turns the text back into a number, 25.5, and the flour is 2 times 25.5: 51 cups. The bug isn't on the paused line at all. It's on line 16, where servings got its value.

Try it. Set the breakpoint on line 10, start debugging, and find servings in the Variables panel. Then add typeof servings and servings / recipeServes to Watch. Do they match what you worked out above?

Step through, one line at a time

When the program is paused, a small toolbar appears at the top of VS Code. Its buttons move the program forward:

Button Key in VS Code What it does
Continue F5 Runs on until the next breakpoint, or to the end
Step Over F10 Runs the highlighted line, then pauses on the next one
Step Into F11 If the highlighted line calls a function, goes inside it and pauses on its first line
Step Out Shift+F11 Runs the rest of the current function and pauses where it was called from
Stop Shift+F5 Ends the program

Press Step Over once: line 10 runs, factor appears in Variables as 25.5, and line 11 is highlighted. Press Continue, and the program pauses at line 10 again, for the milk this time, since scaleCups runs once per ingredient.

To find where a value first goes wrong, start earlier. Move the breakpoint to line 14 (const guestsTyped = "10";), start again, and press Step Over line by line, watching servings. The moment it appears as "102" is the moment you've found the bug. When the highlighted line is the console.log in the loop, press Step Into to go inside scaleCups, and Step Out to come back.

The fix

servings needs a number before it's added to. Number() turns text into a number:

const servings = Number(guestsTyped) + extraHelpings;

With that line in place, party.js prints the right amounts:

flour: 6 cups
milk: 4.5 cups
sugar: 0.75 cups

The same tools in the browser

Code that runs on a web page has a debugger too, in the browser's DevTools (developer tools). Here's the same script on a page of its own. Save it as party.html:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Brunch Party Scaler</title>
</head>
<body>
  <h1>Brunch Party Scaler</h1>
  <p>Open the DevTools Console to see the shopping list.</p>
  <script>
    const recipe = [{ name: "flour", cups: 2 }, { name: "milk", cups: 1.5 }];
    const recipeServes = 4;
    const guestsTyped = "10";
    const servings = guestsTyped + 2;
    for (const item of recipe) {
      console.log(`${item.name}: ${item.cups * (servings / recipeServes)} cups`);
    }
  </script>
</body>
</html>

The page's code goes between <script> and </script>. To debug it:

  1. Open party.html in Chrome or Edge (double-click it, or use VS Code's Live Preview).
  2. Open DevTools: F12, or Ctrl+Shift+I (Windows) or Cmd+Option+I (Mac). Choose the Sources panel and click party.html in the list of files on the left.
  3. Click the line number of the for line. A blue marker appears: that's the breakpoint.
  4. Reload the page (F5 or Ctrl+R, Cmd+R on a Mac). The script runs again and pauses on your line.

On the right, Scope shows the variables (with servings: "102"), and Watch and Call Stack work as in VS Code. The step buttons are the same too, with one difference: Resume (Continue) is F8 here. Firefox has the same tools in its Debugger panel.

You can also pause from inside the code: a line that says debugger; pauses the program right there whenever DevTools is open, or VS Code is debugging. Delete it when you're done.

Try it. Debug party.html in your browser, find "102" in Scope, then fix the page the way you fixed party.js and reload it.

Check yourself

2 questions to try on the lesson page.

So what

Every developer meets bugs that don't crash. With a breakpoint, the Variables panel and Step Over, you stop guessing and watch your program work, until the line where a value goes wrong is right in front of you.

Key terms

Cheat sheets

Related