Find and fix bugs methodically with console logging, error messages and DevTools breakpoints.
Why this matters
Every developer writes bugs; good developers find them quickly. Debugging is not guessing and changing random lines until something works. It is a method: reproduce the problem, read what the computer is telling you, narrow down where it goes wrong, fix it, and check the fix. Your browser already includes powerful tools for this, called DevTools, and learning them early will save you hours every week.
Step 1: read the error message
When JavaScript hits a problem it cannot continue from, it throws an error and prints it in the console (open DevTools with F12, or Ctrl+Shift+I / Cmd+Option+I, then the Console tab). The message usually tells you what and where:
Uncaught TypeError: Cannot read properties of undefined (reading 'name')
at showUser (app.js:12:22)
- TypeError is the kind of error.
- Cannot read properties of undefined (reading 'name') means you wrote
something.nameandsomethingwasundefined. - app.js:12:22 is the file, line and column. Click it to jump there.
Common error types:
| error | usually means |
|---|---|
ReferenceError: x is not defined | a typo in a name, or using a variable outside its scope |
TypeError: x is not a function | calling something that is not a function, often a typo or wrong import |
TypeError: Cannot read properties of null | querySelector found nothing, or data has not loaded |
SyntaxError: Unexpected token | a missing bracket, quote or comma |
Step 2: bugs without errors
The hardest bugs throw no error at all: the code runs and the answer is simply wrong. For those, make the program show you its state. console.log is the simplest tool:
function average(nums) {
let total = 0;
for (let i = 0; i <= nums.length; i++) {
total += nums[i];
console.log("i:", i, "value:", nums[i], "total:", total);
}
return total / nums.length;
}
average([2, 4, 6]);
The log shows i: 3 value: undefined total: NaN. The loop runs one time too many, because it uses <= instead of <. Adding undefined to a number gives NaN, and that NaN spreads to every later result.
Useful console tools:
console.log({ total, i }); // label values by wrapping them in an object
console.table(users); // an array of objects as a table
console.error("bad input"); // shows in red
typeof value; // check the type when results look odd
Remove debugging logs before you commit; they clutter the console for everyone.
Step 3: breakpoints
Logging means editing, running and reading over and over. A breakpoint pauses the code on a line so you can look at everything at once:
- Open DevTools, go to Sources (Chrome/Edge) or Debugger (Firefox) and open your file.
- Click a line number to set a breakpoint, or write
debugger;in your code. - Trigger the code again (reload, or click the button).
- Execution pauses. Hover over variables or read the Scope panel to see their values.
- Use Step over to run one line at a time and watch values change.
Breakpoints are especially useful in event handlers and loops, where printing every value gets noisy.
Step 4: narrow it down
If you do not know where the bug is, cut the problem in half. Log (or pause) halfway through the process. If the value is already wrong there, the bug is earlier; if it is right, the bug is later. Repeat. This divide and conquer approach finds bugs in long code quickly.
Also simplify the input. If a function fails on a list of 500 users, try it with 2. Small inputs make the wrong step obvious.
Three classic bugs
String instead of number. Input values are strings:
function addInputs(a, b) {
return a + b; // addInputs("5", "3") -> "53"
}
// fix: return Number(a) + Number(b);
Assignment instead of comparison:
function isAdult(age) {
if (age = 18) return true; // assigns 18, which is truthy: always true
return false;
}
// fix: if (age >= 18) return true;
Off by one: i <= list.length visits one item past the end, as above.
Step 5: fix and confirm
Once you understand the cause, make the smallest fix, then re-run the case that failed and a few that worked before, to make sure you did not break them. Writing those cases down as checks or tests is how professional teams stop the same bug coming back.
Common mistakes
- Changing code before reading the error. The message and line number are your best clue.
- Fixing the symptom, not the cause. Replacing
NaNwith0at the end hides the real bug. - Logging too little. Log the inputs, not just the output.
- Forgetting to remove
debugger;and logs. - Assuming the data is what you expect. Log it and look.
Quick recap
- Read the error: type, message, file and line.
- For wrong answers, show the state with
console.logor a breakpoint. - Narrow down by halving the code and shrinking the input.
- Fix the cause, then re-check old and new cases.
Try it
Open the Do tab. Three functions each contain one classic bug. Use the console output to find and fix them.