I used to think some developers were just naturally good at debugging. Like they had some kind of instinct for finding bugs that I didn’t have. They’d look at a broken feature, ask two questions, and find the problem in minutes while I was still adding console.log statements everywhere hoping something would make sense.
It took me a while to realize that debugging is a process, not a superpower. Those developers weren’t smarter. They just had a system. And once I started building my own system, I got way faster at finding and fixing bugs.
Start by reproducing the bug
This sounds obvious but you’d be surprised how many people skip it. Before you touch any code, make sure you can reliably reproduce the problem. If you can’t reproduce it, you’re just guessing.
Ask yourself:
- What exactly happens?
- What should happen instead?
- Does it happen every time or just sometimes?
- Does it happen in all browsers/devices or just one?
Half the time, just answering these questions tells you where the bug is. If it only happens in Safari, it’s probably a CSS or API compatibility issue. If it only happens on the first load, it’s probably a race condition or missing initial state.
Read the error message. Actually read it.
I know, I know. But I spent years glancing at error messages and immediately going to Stack Overflow. The error message usually tells you exactly what’s wrong. Not always, but more often than you’d think.
Cannot read property 'name' of undefined means something is undefined that shouldn’t be. Don’t Google it. Ask yourself: what’s undefined, and why?
Module not found: Can't resolve './components/Header' means the file path is wrong or the file doesn’t exist. Check the path. Check the filename. Check for typos.
Most error messages are actually pretty helpful if you slow down and read them properly.
Narrow down where the bug lives
This is the most important skill in debugging. Instead of reading through your entire codebase trying to spot the problem, narrow it down systematically.
The simplest way: add a log at the start of the flow and one at the end. Does it reach the end? If yes, the problem is after. If no, the problem is somewhere in between. Keep splitting until you find the exact line.
It’s basically binary search for bugs.
For frontend issues:
- Does the data arrive correctly from the API? Check the network tab.
- Is the state correct? Check with React DevTools or just log it.
- Is the component rendering? Add a visible border or background color.
For backend issues:
- Does the request reach your server? Check the logs.
- Is the input what you expect? Log the request body.
- Does the database query return the right data? Log the query result.
Each step eliminates a big chunk of possible causes.
Check what changed recently
If something was working yesterday and it’s broken today, something changed. Look at the recent commits. Check if any dependencies were updated. See if someone changed an environment variable or a config file.
git log --oneline -10 and git diff are your best friends here. Most bugs I find at work are caused by a recent change, not some deep architectural flaw.
Don’t assume, verify
This one bit me so many times. I’d look at a piece of code and think “that’s fine, the bug can’t be there” and skip over it. Then after an hour of looking everywhere else, turns out the bug was exactly in the code I skipped.
If you haven’t verified that a piece of code works correctly with a log or a debugger, don’t assume it does. Check everything. Even the stuff that looks obviously correct.
The bug is always in the place you’re not looking.
Use the debugger sometimes
I’ll be honest, I use console.log for 80% of my debugging. It’s fast and it works. But for complex issues where you need to step through code line by line, the debugger is worth it.
Set a breakpoint, inspect the variables at each step, and watch exactly what happens. It’s slower but sometimes you catch things that logging would miss, like a variable being the right value but the wrong type.
VS Code has great debugging built in for both frontend and backend JavaScript. It takes 5 minutes to set up and it’s worth learning.
Rubber duck it
When I’m really stuck, I explain the problem out loud. To myself. To a coworker. To nobody. It doesn’t matter. The act of explaining what should happen step by step forces you to think clearly about it.
I can’t tell you how many times I’ve started explaining a bug and realized the answer halfway through the sentence. Your brain works differently when you’re explaining vs when you’re just staring at code.
If you feel weird talking to yourself, write it out. Open a note and describe the bug like you’re filing a bug report for someone else. Same effect.
Take a break if you’re stuck
I used to force myself to keep staring at the screen when I was stuck. That almost never works. Your brain gets tunnel vision and you keep looking at the same code the same way.
Walk away. Get coffee. Go outside for 10 minutes. Work on something else. The number of times I’ve found a bug immediately after coming back from a break is honestly ridiculous. Your brain keeps processing in the background.
If it’s late and you’ve been stuck for over an hour, just stop. Sleep on it. Tomorrow morning you’ll probably find it in 5 minutes.
It gets easier with practice
Debugging isn’t something you’re born good at. It’s a skill that improves every time you fix a bug. You build pattern recognition. You learn the common causes. You develop instincts about where to look first.
The difference between a junior and senior developer isn’t that the senior writes perfect code. It’s that when something breaks, they find it faster because they’ve seen similar bugs before.
So next time you’re stuck on a bug, don’t feel bad about it. You’re literally practicing one of the most important skills in software development.