Debugging is an essential skill—you'll spend significant time finding and fixing bugs. Professional developers don't just write code; they master the tools and techniques to troubleshoot issues efficiently. Browser DevTools provide powerful debugging capabilities: breakpoints to pause execution, console methods for logging, network monitors, and performance profilers. Learning to debug effectively transforms frustrating bugs into solvable puzzles. Let's master debugging!
Browser DevTools Overview
Opening DevTools:
- Windows/Linux: F12 or Ctrl+Shift+I
- Mac: Cmd+Opt+I
- All: Right-click → Inspect
Main Panels:
- Console: View logs, errors, run JavaScript
- Sources: Debug code with breakpoints
- Network: Monitor HTTP requests
- Elements: Inspect and modify HTML/CSS
- Application: View localStorage, cookies, etc.
- Performance: Profile code execution
Console Methods
Basic Logging
// console.log() - Basic logging
console.log('Simple message');
console.log('User:', user);
console.log('Multiple', 'values', 123, true);
// String interpolation
let name = 'Alice';
let age = 25;
console.log(`User: ${name}, Age: ${age}`);
// console.warn() - Warning messages (yellow)
console.warn('This is a warning');
console.warn('API rate limit approaching:', remainingCalls);
// console.error() - Error messages (red)
console.error('This is an error');
console.error('Failed to load user:', error);
// console.info() - Info messages (blue in some browsers)
console.info('Application started');
console.info('Version:', appVersion);Advanced Console Methods
// console.table() - Display as table
let users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 },
{ id: 3, name: 'Charlie', age: 28 }
];
console.table(users);
// Displays formatted table in console
// console.group() - Group related logs
console.group('User Details');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.log('Age:', user.age);
console.groupEnd();
// Nested groups
console.group('Processing Order');
console.log('Order ID:', order.id);
console.group('Items');
order.items.forEach(item => {
console.log(`- ${item.name}: $${item.price}`);
});
console.groupEnd();
console.log('Total:', order.total);
console.groupEnd();
// console.time() & console.timeEnd() - Measure execution time
console.time('Data Processing');
processLargeDataset();
console.timeEnd('Data Processing');
// Output: "Data Processing: 234.567ms"
// console.count() - Count occurrences
function processItem(item) {
console.count('processItem called');
// Process item...
}
processItem('a'); // processItem called: 1
processItem('b'); // processItem called: 2
processItem('c'); // processItem called: 3
// console.trace() - Show call stack
function functionA() {
functionB();
}
function functionB() {
functionC();
}
function functionC() {
console.trace('Trace from functionC');
}
functionA();
// Shows: functionC → functionB → functionA
// console.assert() - Log only if condition is false
let age = 15;
console.assert(age >= 18, 'User must be 18 or older');
// Only logs if age < 18
// console.clear() - Clear console
console.clear();Styled Console Output
// Add CSS styles to console output
console.log(
'%cImportant Message',
'color: red; font-size: 20px; font-weight: bold;'
);
console.log(
'%cSuccess: %cOperation completed',
'color: green; font-weight: bold;',
'color: black;'
);
// Multiple styles
console.log(
'%cError%c in %cfunction processData',
'color: white; background: red; padding: 2px 5px;',
'color: black;',
'color: blue; font-weight: bold;'
);
// Useful for debugging
function debug(category, message) {
let styles = {
error: 'color: white; background: red; padding: 2px 5px;',
warning: 'color: black; background: yellow; padding: 2px 5px;',
info: 'color: white; background: blue; padding: 2px 5px;',
success: 'color: white; background: green; padding: 2px 5px;'
};
console.log(`%c${category}`, styles[category], message);
}
debug('error', 'Failed to load data');
debug('success', 'User created successfully');Breakpoints and Debugging
Setting Breakpoints
// Method 1: DevTools UI
// 1. Open DevTools (F12)
// 2. Go to Sources tab
// 3. Find your file
// 4. Click line number to set breakpoint
// Method 2: debugger statement
function processOrder(order) {
debugger; // Pauses here when DevTools is open
let total = 0;
for (let item of order.items) {
total += item.price * item.quantity;
}
return total;
}
// Method 3: Conditional breakpoints
// In DevTools, right-click line number → Add conditional breakpoint
// Example condition: order.total > 1000
// Method 4: Logpoints (no pause, just log)
// Right-click line number → Add logpoint
// Example: Log message "Processing order {order.id}"Stepping Through Code
Debugger Controls:
- Resume (F8): Continue to next breakpoint
- Step Over (F10): Execute current line, skip function calls
- Step Into (F11): Enter function calls
- Step Out (Shift+F11): Exit current function
- Step: Execute one statement
function calculateTotal(items) {
debugger; // Execution pauses here
let total = 0;
// F10 (Step Over) - executes loop without entering
for (let item of items) {
// F11 (Step Into) - enters calculatePrice function
total += calculatePrice(item);
}
return total;
}
function calculatePrice(item) {
// Shift+F11 (Step Out) - finishes function and returns
return item.price * item.quantity;
}
// When paused, you can:
// - Hover over variables to see values
// - Use console to run JavaScript
// - Check call stack
// - View scope variablesWatch Expressions
// In DevTools Sources panel, use Watch section to monitor values
function processData(users) {
debugger;
// Add to Watch:
// - users.length
// - users[0].age
// - users.filter(u => u.age > 25).length
let adults = users.filter(u => u.age >= 18);
// Watch expressions update as you step through code
return adults;
}
// Watch expressions can be:
// - Variable names: user, count, total
// - Property access: user.name, order.items[0]
// - Expressions: users.length > 10, total * 0.1
// - Function calls: isValid(data), formatDate(date)Common Debugging Strategies
1. Isolate the Problem
// Strategy: Binary search through code
// Problem: Function returns wrong value
function calculateDiscount(price, userType, quantity) {
let discount = 0;
console.log('Input:', { price, userType, quantity });
if (userType === 'premium') {
discount = 0.2;
} else if (userType === 'regular') {
discount = 0.1;
}
console.log('Base discount:', discount);
if (quantity > 10) {
discount += 0.05;
}
console.log('Final discount:', discount);
let finalPrice = price * (1 - discount);
console.log('Final price:', finalPrice);
return finalPrice;
}
// Add logs to narrow down where bug occurs
// Remove half the logs, see if bug persists
// Repeat until you find the exact line2. Rubber Duck Debugging
// Explain code line-by-line to a rubber duck (or colleague)
// Often reveals the bug while explaining
function processPayment(order) {
// "First, I get the user's payment method"
let paymentMethod = getPaymentMethod(order.userId);
// "Then I calculate the total with tax"
let total = calculateTotal(order.items);
// "Wait... I forgot to apply the discount!"
// Bug found: Missing discount calculation
let discount = getDiscount(order);
total = total - discount;
// "Now I charge the payment method"
return charge(paymentMethod, total);
}3. Check Assumptions
// Don't assume - verify!
function getUserById(id) {
// Assumption: API returns user object
// Reality: Might return null, might throw error
console.log('Fetching user:', id);
console.log('Type of id:', typeof id); // Is it a number or string?
let user = api.getUser(id);
console.log('API returned:', user);
console.log('Type:', typeof user);
console.log('Is null?', user === null);
console.log('Is array?', Array.isArray(user));
// Verify assumptions about data structure
if (user) {
console.log('User has name?', 'name' in user);
console.log('User keys:', Object.keys(user));
}
return user;
}
// Common assumption bugs:
// - API returns object, but returns array
// - Variable is number, but it's string
// - Array has items, but it's empty
// - Async operation completed, but it hasn't4. Minimal Reproducible Example
// Create smallest possible code that reproduces bug
// Original (100 lines, hard to debug)
function complexFunction(data) {
// ... lots of code
// Bug somewhere in here
// ... more code
}
// Minimal example (10 lines, easy to debug)
function minimalExample() {
let data = { items: [1, 2, 3] };
// This line causes the bug
let result = data.items.map(item => item * 2);
// Expected: [2, 4, 6]
// Actual: undefined
// Because: forgot to return from arrow function
return result;
}
// Benefits:
// - Easier to share when asking for help
// - Faster to identify root cause
// - Can test fixes in isolationDebugging Async Code
// Async debugging is tricky - here's how to handle it
// 1. Log promise states
async function fetchData() {
console.log('Starting fetch...');
try {
let response = await fetch('/api/data');
console.log('Response received:', response.status);
let data = await response.json();
console.log('Data parsed:', data);
return data;
} catch (error) {
console.error('Error occurred:', error);
throw error;
}
}
// 2. Use debugger with async
async function processData() {
debugger; // Pauses here
let data = await fetchData(); // Step Over to skip fetch details
debugger; // Pauses after fetch completes
return processResults(data);
}
// 3. Track async flow
async function complexAsyncFlow() {
console.log('1. Starting');
let users = await fetchUsers();
console.log('2. Users fetched:', users.length);
let orders = await fetchOrders(users);
console.log('3. Orders fetched:', orders.length);
let processed = await processOrders(orders);
console.log('4. Orders processed:', processed);
return processed;
}
// 4. Debug race conditions
async function handleRaceCondition() {
console.log('Request 1 starting...');
let request1 = fetchData(1);
console.log('Request 2 starting...');
let request2 = fetchData(2);
// Which completes first?
let result1 = await request1;
console.log('Request 1 completed:', result1);
let result2 = await request2;
console.log('Request 2 completed:', result2);
}
// 5. Async breakpoints
async function debugAsyncBreakpoint() {
// Set breakpoint here
let data = await fetch('/api/data');
// When paused, use console to check:
// - data
// - data.status
// - await data.json() // Run async operations in console!
return data;
}Network Debugging
// Use Network tab to debug API issues
// 1. Monitor requests
async function debugAPICall() {
// Open DevTools Network tab before running
console.log('Making API call...');
try {
let response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({
name: 'Alice',
email: 'alice@test.com'
})
});
// In Network tab, click request to see:
// - Request headers
// - Request body
// - Response headers
// - Response body
// - Timing information
console.log('Status:', response.status);
console.log('Headers:', response.headers);
let data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
console.error('Request failed:', error);
// Check Network tab for:
// - CORS errors
// - 404/500 errors
// - Network timeouts
throw error;
}
}
// 2. Simulate slow network (DevTools → Network → Throttling)
// Test with: Fast 3G, Slow 3G, Offline
// 3. Preserve log across navigation
// Network tab → ☑ Preserve log
// 4. Copy request as fetch/curl
// Network tab → Right-click request → Copy → Copy as fetchCommon JavaScript Errors
// 1. TypeError: Cannot read property 'X' of undefined
let user = null;
console.log(user.name); // TypeError!
// Fix: Check if exists
if (user && user.name) {
console.log(user.name);
}
// Or use optional chaining
console.log(user?.name);
// 2. ReferenceError: X is not defined
console.log(userName); // ReferenceError!
// Fix: Declare variable
let userName = 'Alice';
console.log(userName);
// 3. TypeError: X is not a function
let sum = 42;
sum(5, 3); // TypeError! sum is a number, not a function
// Fix: Check type
if (typeof sum === 'function') {
sum(5, 3);
}
// 4. SyntaxError: Unexpected token
let obj = {
name: 'Alice'
age: 25 // Missing comma! SyntaxError
};
// Fix: Add comma
let obj = {
name: 'Alice',
age: 25
};
// 5. Infinite loop (browser hangs)
let i = 0;
while (i < 10) {
console.log(i);
// Forgot to increment! Infinite loop
}
// Fix: Update loop variable
let i = 0;
while (i < 10) {
console.log(i);
i++; // Don't forget this!
}
// 6. Async not awaited
async function getData() {
let data = fetch('/api/data'); // Missing await!
console.log(data); // Logs Promise, not data
}
// Fix: Use await
async function getData() {
let data = await fetch('/api/data');
console.log(data);
}
// 7. Wrong comparison operator
let x = '5';
if (x == 5) { // True (type coercion)
console.log('Equal');
}
// Better: Use strict equality
if (x === 5) { // False (different types)
console.log('Strictly equal');
}Debugging Checklist
When you encounter a bug:
- Read the error message carefully
- Check the line number where error occurred
- Inspect the call stack
- Add console.logs around the problem area
- Set breakpoints to pause execution
- Check variable values and types
- Verify your assumptions
- Isolate the problem (minimal example)
- Check Network tab for API issues
- Search the error message online
- Take a break if stuck
- Ask for help with specific details
Key Takeaways
- DevTools is your debugging superpower
- Use console methods beyond console.log()
- Breakpoints pause execution for inspection
- Step through code to understand flow
- debugger statement creates programmatic breakpoints
- Network tab shows all HTTP requests
- Read error messages carefully—they tell you what's wrong
- Isolate problems with minimal examples
- Verify assumptions, don't guess
- Check variable types and values
- Practice makes debugging faster and easier
What's Next?
You now understand debugging techniques and can use browser DevTools effectively! You've learned console methods, breakpoints, stepping through code, and professional troubleshooting strategies.
In the final lesson, we'll build a Complete Interactive Project—bringing together everything you've learned in this tutorial series to create a real-world application!
💪 Practice Challenge:
Before moving on, try:
- Set breakpoints in your code and step through execution
- Use console.table() to visualize data
- Debug an async function with breakpoints
- Use Network tab to inspect API calls
- Practice isolating bugs with minimal examples