How to Debug JavaScript Like a Pro
Stop using console.log for everything. Here's a better way.
Level 1: console.log Done Right
// ❌ Bad: No context
console.log(data);
console.log(user);
console.log(error);
// ✅ Good: Labeled and structured
console.log('User data:', { id: user.id, name: user.name, role: user.role });
console.log('API response status:', res.status);
// ✅ Better: Console styling
console.log('🚀 App started');
console.log('⚠️ Warning: Rate limit approaching', { remaining: limit });
console.error('❌ Fetch failed:', error.message);
// ✅ Best: Table format for arrays/objects
const users = [{name:'Alex',age:30}, {name:'Sam',age:25}];
console.table(users);
// ┌─────────┬──────┬─────┐
// │ (index) │ name │ age │
// ├─────────┼──────┼─────┤
// │ 0 │ Alex │ 30 │
// │ 1 │ Sam │ 25 │
// └─────────┴──────┴─────┘
Level 2: Console Methods You Didn't Know About
// Group related logs
console.group('API Request');
console.log('URL:', url);
console.log('Method:', method);
console.log('Headers:', headers);
console.groupEnd();
// Assert (only logs if condition is false)
console.assert(user.age >= 18, 'User is underage:', user.age);
// Assertion failed: User is underage: 15
// Time operations
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData'); // fetchData: 1.234s
// Count how many times something happens
function handleRequest(req) {
console.count('requests'); // requests: 1, 2, 3...
}
// Trace the call stack
function process(data) {
console.trace('process called');
// Shows full stack trace of who called this function
}
// Dir: Inspect DOM elements or objects
console.dir(document.body); // Full object properties
console.dirxml(element); // XML/HTML representation
Level 3: Chrome DevTools Breakpoints
// Don't modify code! Use DevTools instead:
// Regular breakpoint — click line number in Sources panel
debugger; // Programmatic breakpoint (remove before commit!)
// Conditional breakpoint — right-click → "Add conditional breakpoint"
// Enter: data.length > 1000
// Only pauses when condition is true!
// Logpoint — right-click → "Add logpoint"
// Enter: 'Processing item:', item.name
// Logs without pausing execution!
// DOM breakpoint — Elements panel → right-click element
// Break on: subtree modifications, attribute changes, node removal
// XHR/Fetch breakpoint — Sources panel → XHR breakpoints
// Add URL pattern: /api/users
// Pauses when any request matches this URL
Level 4: Network Tab Mastery
When debugging API issues:
1. Open DevTools → Network tab
2. Reproduce the issue
3. Find the request in the list
4. Check:
- Status code (200? 404? 500?)
- Response headers (CORS? Content-Type?)
- Response body (is it what you expected?)
- Timing (DNS? TTFB? Download?)
Pro tips:
- Filter by type: XHR, JS, CSS, Img
- Filter by text: type in search box
- "Preserve log" to keep logs across page navigations
- "Disable cache" to test without cached responses
- Copy as cURL/fetch to reproduce programmatically
Level 5: The Debugger Statement + Source Maps
// In your source code:
function complexCalculation(input) {
debugger; // Execution pauses here when DevTools is open
const result = input * 2;
return result;
}
DevTools workflow:
1. Open Sources panel
2. Find your file (or Ctrl+P to search)
3. Set breakpoints by clicking line numbers
4. Refresh page or trigger action
5. Use controls:
- F8: Resume / Pause
- F10: Step over (don't go into functions)
- F11: Step into (go inside functions)
- Shift+F11: Step out (exit current function)
6. Watch panel: Add expressions to monitor
7. Scope panel: See current variables
8. Call Stack: See how you got here
Level 6: Performance Profiling
// Performance tab:
// 1. Click Record (or Ctrl+E)
// 2. Do the thing you want to measure
// 3. Stop recording
// 4. Analyze the flame chart
// Common findings:
// - Long tasks (>50ms) cause jank
// - Layout thrashing (read/write/read/write DOM)
// - Memory leaks (growing heap over time)
// - Unnecessary re-renders
// Memory tab:
// 1. Take heap snapshot
// 2. Do actions that might leak
// 3. Take another snapshot
// 4. Compare snapshots to find leaked objects
Common Bugs & How to Find Them
"undefined is not a function"
// Cause: Calling something that doesn't exist
user.getName(); // getName is undefined
// Fix: Check what you're calling
console.log(typeof user.getName); // "undefined"?
console.log(user); // Is user what you expect?
"Cannot read property of undefined"
// Cause: Chaining on possibly undefined value
data.user.address.city;
// Fix: Optional chaining
data?.user?.address?.city; // Returns undefined instead of crashing
// Or destructure with defaults
const { city = 'Unknown' } = data?.user?.address ?? {};
"TypeError: X is not a function"
// Cause: Variable shadows a function name
const fetch = 'not a function';
fetch('/api/data'); // TypeError!
// Fix: Name things differently
const fetchUrl = '/api/data';
fetch(fetchUrl);
Race Condition
// Cause: Async operation completes out of order
let userId;
fetchUser().then(u => { userId = u.id });
loadData(userId); // userId might still be undefined!
// Fix: Chain properly
fetchUser()
.then(u => loadData(u.id))
.catch(err => console.error(err));
The Debugging Checklist
When something breaks:
1. Reproduce it consistently
2. Read the error message carefully
3. Check the line number and file
4. Verify assumptions (what IS the value of this variable?)
5. Simplify (comment out code until it works)
6. Check recent changes (git diff)
7. Search for the error message online
8. Ask: What changed since it last worked?
9. Check browser console AND server logs
10. Clear cache and cookies (sometimes it's that simple)
What's your favorite debugging technique? Share it below!
Follow @armorbreak for more developer content.
Top comments (0)