Error Handling and Debugging · Lesson 30 of 40
Debugging JavaScript with Console and DevTools
Every developer spends a big part of their day debugging. The difference between a beginner and a professional isn't writing bug-free code – it's finding bugs quickly and methodically. This lesson covers the console beyond console.log, the browser DevTools debugger, and a simple process that works for any bug.
In this lesson
- Console methods: table, group, time, count, assert
- Breakpoints and the
debuggerstatement - Network and performance panels
- A step-by-step method for hunting bugs
The console, beyond log
const user = { id: 7, name: "Asha", roles: ["admin"] };
console.log("user:", user);
console.log({ user }); // shorthand: shows the variable name too
console.info("Info message");
console.warn("Low stock"); // yellow in browsers, stderr in Node
console.error("Payment failed"); // red, with a stack trace in browsers
console.group("Checkout");
console.log("validate cart");
console.log("charge card");
console.groupEnd();
for (const item of ["pen", "pen", "bag"]) console.count(item);
console.assert(user.roles.includes("admin"), "user should be admin"); // silent when true
user: { id: 7, name: 'Asha', roles: [ 'admin' ] }
{ user: { id: 7, name: 'Asha', roles: [ 'admin' ] } }
Info message
Low stock
Payment failed
Checkout
validate cart
charge card
pen: 1
pen: 2
bag: 1console.table and console.time
const scores = [
{ name: "Asha", js: 91, sql: 78 },
{ name: "Ravi", js: 64, sql: 88 },
];
console.table(scores);
console.time("sort 100k");
Array.from({ length: 100_000 }, () => Math.random()).sort((a, b) => a - b);
console.timeEnd("sort 100k"); // prints the elapsed time, e.g. "sort 100k: 31.2ms"
┌─────────┬────────┬────┬─────┐ │ (index) │ name │ js │ sql │ ├─────────┼────────┼────┼─────┤ │ 0 │ 'Asha' │ 91 │ 78 │ │ 1 │ 'Ravi' │ 64 │ 88 │ └─────────┴────────┴────┴─────┘ sort 100k: 31.2ms
Logging objects in the browser
Browser consoles show objects live: if the object changes after you log it, expanding it later shows the new values. To capture a snapshot, log structuredClone(obj) or JSON.stringify(obj).
Breakpoints: pause and inspect
console.log shows what you guessed to print. A breakpoint pauses the code so you can inspect everything: all variables, the call stack, and step line by line.
Using the DevTools debugger
- Open DevTools → SourcesF12, then find your file (Ctrl/⌘+P)
- Click a line numbersets a breakpoint; reload or trigger the code
- Code pauseshover variables; check Scope and Call Stack panels
- Stepover (F10), into (F11), out (Shift+F11), resume (F8)
- Fix and repeatedit, reload, confirm the fix
function applyCoupon(cart, code) {
const discount = code === "SAVE10" ? 0.1 : 0;
debugger; // pauses here when DevTools is open
return cart.total - cart.total * discount;
}
Useful breakpoint types:
- Conditional breakpoint – right-click a line number: pause only when
order.id === 1042. Perfect inside loops. - Logpoint – logs a message without editing code or pausing.
- DOM breakpoint – pause when an element changes (Elements panel → right-click → Break on).
- Event listener / XHR breakpoints – pause on any click, or any request to a URL.
- Pause on exceptions – stop exactly where an error is thrown.
Node.js has the same debugger: run node --inspect app.mjs and open chrome://inspect, or use VS Code's Run and Debug.
Other DevTools panels
| Panel | Use it to |
|---|---|
| Elements | inspect and edit HTML/CSS live, see event listeners |
| Console | run code in the page's context, see errors |
| Network | see every request: URL, status, headers, payload, timing; throttle to "Slow 4G" |
| Application | inspect localStorage, cookies, service workers |
| Performance / Lighthouse | find slow code, long tasks and page-speed problems |
A method for any bug
Debug methodically
- Reproducefind exact steps that make it happen every time
- Read the errortype, message, file and line in the stack trace
- Narrow it downwhere is the value first wrong? breakpoint and inspect
- Form a hypothesis"total is a string because input.value is a string"
- Fix and verifyconfirm the fix, check nearby cases, add a test
Here's that method on a real bug. The cart total shows "₹5001000" instead of "₹1500":
const prices = ["500", "1000"]; // values read from form inputs: strings!
const buggyTotal = prices.reduce((sum, p) => sum + p, 0);
console.log(buggyTotal, typeof buggyTotal); // hypothesis confirmed: string joining
const total = prices.reduce((sum, p) => sum + Number(p), 0);
console.log(total, typeof total);
05001000 string 1500 number
More professional habits
- Explain the code line by line to someone (or a rubber duck) – you'll often spot the bug yourself.
- Use
git bisectto find which commit introduced a bug. - Turn on ESLint and TypeScript – they catch whole classes of bugs before the code runs.
- Remove debug logs and
debuggerstatements before committing.
Practice
Try it yourself
Find the bug: this should print the average of the scores, but prints
NaN.Show solution
const scores = { asha: 90, ravi: 70 }; // Bug: Object.keys gives names, not numbers – use Object.values const values = Object.values(scores); const avg = values.reduce((a, b) => a + b, 0) / values.length; console.log(avg);80
Quick check
What does the debugger statement do?
Pauses execution at that line when developer tools are open.
When is a conditional breakpoint useful?
Inside loops or frequently called code, to pause only for the case you care about.
Which console method is best for an array of objects?
console.table.
Summary
- Use console.table, group, time, count and assert alongside log.
- Breakpoints (including conditional ones) show every variable and the call stack.
- Reproduce → read the error → narrow down → hypothesise → fix and verify.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.