Find and fix bugs faster using the console methods, breakpoints, the debugger statement and a calm reading of error messages.

Bishal Biswas
Self Learner
Debugging is normal
Every programmer writes bugs. The difference between a beginner and an experienced developer is not fewer bugs, it is finding them faster.
Beyond console.log
console.log('A normal message');
console.info('Extra information');
console.warn('Something looks wrong');
console.error('Something broke');
Warnings and errors show with an icon and a stack trace, which makes them stand out in a busy console.
Label your logs
const user = { name: 'Asha', age: 30 };
console.log(user);
console.log('user:', user);
console.log({ user });
The last form prints { user: {...} }, so the variable name is included automatically. It is a small habit that saves a lot of squinting.
console.table
For arrays of objects, this is far better than a plain log.
const students = [
{ name: 'Asha', score: 92 },
{ name: 'Ben', score: 78 },
{ name: 'Chitra', score: 85 }
];
console.table(students);
You get a real table with sortable columns.
Grouping
console.group('User details');
console.log('Name: Asha');
console.log('Age: 30');
console.groupEnd();
Timing
console.time('loop');
let total = 0;
for (let i = 0; i < 1000000; i++) {
total += i;
}
console.timeEnd('loop');
The label in time and timeEnd must match.
Counting
function handleClick() {
console.count('clicks');
}
handleClick();
handleClick();
Useful for checking whether a function runs more often than you expected.
Assertions
const age = -5;
console.assert(age > 0, 'Age should be positive, got', age);
Prints only when the condition is false, so it stays quiet when things are fine.
Reading an error message
Errors look intimidating until you learn the shape.
// TypeError: Cannot read properties of null (reading 'textContent')
// at showTitle (script.js:14:23)
// at script.js:20:1
Read it in three parts:
- The type:
TypeErrortells you what kind of problem it is. - The message: you tried to read
textContentfrom something that wasnull. - The location: file
script.js, line 14, column 23.
Go to line 14. Something you expected to be an element was null, which almost always means querySelector did not match.
The errors you will meet most
Cannot read properties of null
const el = document.querySelector('#missing');
console.log(el.textContent);
The selector matched nothing. Check your spelling, and check the script runs after the element exists.
x is not a function
const name = 'Ravi';
name.toUppercase();
Usually a typo in the method name. It is toUpperCase with a capital C.
x is not defined
A misspelled variable, or one used outside its scope.
Unexpected token
A syntax problem, normally a missing bracket or brace a line or two above where it points.
Breakpoints
console.log shows you one value. A breakpoint pauses the whole program so you can inspect everything.
- Open DevTools with
F12. - Go to the Sources tab.
- Open your file and click a line number.
- Reload or trigger the code.
While paused you can hover any variable to see its value, look at the Scope panel for everything currently in scope, and use the step buttons to move forward one line at a time.
The debugger statement
function calculateTotal(items) {
let total = 0;
debugger;
for (const item of items) {
total += item.price;
}
return total;
}
When DevTools is open, execution pauses right there. Remember to delete these lines before shipping.
A method that works
When something is broken:
- Read the error properly. The line number is usually right.
- Check your assumptions. Log the value just before the failing line. Nine times out of ten it is not what you expected.
- Narrow it down. Comment out half the code. Is the bug still there? Repeat.
- Explain it out loud. Describing the problem to someone, or even to a rubber duck, makes you spot it surprisingly often.
- Change one thing at a time. Changing three things at once means you will not know which fixed it.
Before you ship
Leftover logs leak information and clutter the console for everyone.
const DEBUG = true;
function log(...args) {
if (DEBUG) {
console.log(...args);
}
}
log('Only shown while debugging');
Flip one flag and they all go quiet.
Quick recap
console.warn,error,table,group,timeandcounteach beat a plain log in the right situation.console.log({ value })labels the output with the variable name.- Errors tell you the type, the reason, and the exact line.
Cannot read properties of nullnearly always means a selector matched nothing.- Breakpoints and
debuggerlet you inspect every variable at a moment in time. - Change one thing at a time, and remove debug logs before shipping.
// Log levels stand out differently in the console
console.log('A normal message');
console.warn('Something looks wrong');
console.error('Something broke');
// Wrapping in braces labels the output with the variable name
const user = { name: 'Asha', age: 30 };
console.log({ user });
// Tables are far easier to read than raw arrays of objects
const students = [
{ name: 'Asha', score: 92 },
{ name: 'Ben', score: 78 },
{ name: 'Chitra', score: 85 }
];
console.table(students);
// Grouping keeps related logs together
console.group('User details');
console.log('Name:', user.name);
console.log('Age :', user.age);
console.groupEnd();
// Timing a slow block
console.time('loop');
let total = 0;
for (let i = 0; i < 1000000; i++) total += i;
console.timeEnd('loop');
// Counting how often something runs
function handleClick() { console.count('clicks'); }
handleClick();
handleClick();
// Assertions stay quiet unless something is wrong
const age = -5;
console.assert(age > 0, 'Age should be positive, got', age);
// A debug flag you can switch off before shipping
const DEBUG = true;
function log(...args) {
if (DEBUG) console.log('[debug]', ...args);
}
log('Only visible while debugging');
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready