Debugging Techniques
Learn systematic approaches to finding and fixing bugs in your code
Sprout presents
Seven systematic steps, from reading the error message to explaining it to a rubber duck. Methodical debugging is wildly underrated.Seven systematic steps, from reading the error message to explaining it to a rubber duck. Methodical debugging is wildly underrated.

Every developer spends a TON of time debugging. Experienced developers still write buggy code (everybody does). What they've got that beginners don't yet is a system for hunting bugs down and fixing them faster.
Debugging is detective work. You've got a crime (the bug), some evidence (error messages and weird behavior), and a culprit to find (the broken code). Good detectives follow a process instead of guessing at random.
Step 1: Read the Error Message#
Sounds obvious, right? But a LOT of beginners see red text, panic, and never actually read the message. Error messages are your best friend. Most of the time they tell you exactly what went wrong and where.
TypeError: Cannot read properties of undefined (reading 'name')
at UserCard (src/components/UserCard.tsx:15:28)
at renderWithHooks (node_modules/react-dom/...)Here's what that error is telling you.
- What happened - You tried to read
.nameon something that'sundefined - Where it happened -
UserCard.tsx, line 15, column 28 - The call stack - The path the code took to get there
Step 2: Reproduce the Bug#
Before you can fix a bug, you need to be able to make it happen again. If you can't trigger it on demand, you have no way to prove your fix actually worked.
Ask yourself a few questions.
- What exact steps trigger the bug?
- Does it happen every time or only sometimes?
- Does it depend on specific data or conditions?
- Does it happen in all browsers or just one?
// Write a test that reproduces the bug
describe('UserCard', () => {
it('should handle undefined user gracefully', () => {
// This should NOT crash
expect(() => render(<UserCard user={undefined} />)).not.toThrow();
});
});Step 3: Strategic Console Logging#
When you need to see what's going on inside your code, console.log is quick and REALLY handy. Just be strategic about it, instead of sprinkling logs everywhere like confetti.
async function processOrder(orderId: string) {
console.log('1. Starting processOrder with:', orderId);
const order = await getOrder(orderId);
console.log('2. Got order:', JSON.stringify(order, null, 2));
const total = calculateTotal(order.items);
console.log('3. Calculated total:', total);
const payment = await chargeCard(order.userId, total);
console.log('4. Payment result:', payment);
return payment;
}Step 4: Binary Search Debugging#
No clue where the bug is hiding? Use binary search. Comment out half your code. Does the bug still happen? If yes, it's in the half that's still running. If no, it's in the half you commented out. Keep repeating until you've cornered it.
async function complexFunction() {
const step1 = await doStep1(); // Comment out bottom half first
const step2 = transform(step1);
// --- If bug happens above this line, it's in the top half ---
const step3 = await doStep3(step2);
const result = format(step3);
return result;
}Every round cuts the search area in half, so even in a 1000 line file you'll find the bug in about 10 steps. Math is cool sometimes.
Step 5: Rubber Duck Debugging#
This one sounds goofy, and it works shockingly well. Explain your code line by line to an object that can't talk back (traditionally a rubber duck). Just explaining it out loud often makes the bug jump out at you.
Here's why it works.
- When you read code, your brain fills in what it expects to see
- When you explain code out loud, you're forced to think about what each line ACTUALLY does
- The gap between "what you expect" and "what actually happens" is where bugs live
Step 6: Check Your Assumptions#

Most bugs come from something you assumed that turned out to be wrong. So question EVERY assumption you've got about the code.
Real example, and not even a code one. An abandoned client project that nobody was touching anymore suddenly spiked in Search Console. Every normal explanation you'd reach for was wrong. The actual cause? The client's fired ex-employee got arrested on the local Florida news... wearing a company shirt with the logo and web address right there on camera. You can't make this stuff up. The data was right the whole time. It was the assumptions that were off.
// Assumption: user is always an object
// Reality: user might be null after logout
function getDisplayName(user) {
return user.firstName + ' ' + user.lastName; // CRASH if user is null
}
// Fixed: check the assumption
function getDisplayName(user) {
if (!user) return 'Guest';
return user.firstName + ' ' + user.lastName;
}Some assumptions that bite people all the time.
- "This API always returns data" (it might return an error)
- "This array always has items" (it might be empty)
- "This value is always a number" (it might be a string from form input)
- "This code runs in order" (async code might not)
Step 7: Use Version Control#
If something worked yesterday and it's broken today, use Git to figure out what changed.
# See the last 10 commits, one line each
git log --oneline -10
# See the actual changes in a commit
git show abc1234
# Compare current code to a working version
git diff HEAD~5
# Find which commit introduced the bug
git bisect start
git bisect bad # Current version is broken
git bisect good abc1234 # This older commit worked
# Git will guide you through a binary search of commitsTL;DR#
- Read the whole error message before you dive into the code
- Make the bug happen reliably before you try to fix it
- Use console.log on purpose, with numbered steps
- Binary search debugging cuts your search area in half every round
- Rubber duck debugging makes you think hard about every line
- Question your assumptions about what the code is doing
- Use Git to find when and where a bug snuck in
What's Next?#
That's the debugging mindset. Next up are the actual tools that make debugging faster and a lot more visual, including your browser's DevTools...
This lesson ends with a short activity.