← Back to posts
Hey Friends!
I found that some of my developer friends were having trouble debugging, and while it is a skill that we are supposed to be good at. It’s perfectly okay if you need help still.
So, this is for you. Especially since we are in the world of AI, it’s easy to find the answer easily, but this skill can help prevent a lot of headaches.
So, here is what is inside:
Why most developers debug emotionally instead of systematically
How to actually read an error message
What “reproducing a bug” really means and why it matters
How to follow a stack trace downward to the real problem
The difference between fixing a symptom and fixing a cause
If this kind of no-fluff engineering content is your thing, subscribe and come build with us.
So, Something breaks, and Panic sets in. Totally Normal.
You stare at the screen, change something, run it, and hope. Maybe change something else, run it, hope harder. Copy the error into Google. Try the first Stack Overflow answer. Try the second one. But now, nothing works, and now you have three new problems.
That was me for a long time. And honestly, it is most developers at some point.
The problem is not intelligence. It is the method. Nobody teaches you how to debug. They teach you syntax, frameworks, patterns. Then they hand you a broken codebase and expect you to figure it out.
Here is what seniors actually do.
Debug Systematically, Not Emotionally
When something breaks, your instinct is to start changing things. Resist that. Yes, I know it’s hard.
Treat a bug like a crime scene. Nothing gets touched until you understand what you are looking at. The goal before writing a single fix is to understand exactly what happened, where, and why.
Step 1: Read the Error Before You Google It
Most developers immediately open a new tab to find the solution. The problem is that the answer is usually right in front of you.
Error messages have two parts that matter:
The what - what the system experienced
TypeError: Cannot read properties of undefined (reading 'map')
The where - where the system experienced it
at ProductList (components/ProductList.jsx:23:14)
The where is almost always more useful than the what. The what is just a symptom. The where puts you at the crime scene.
Go to that file and that line number before you do anything else. Half the time, the answer is right there.
Step 2: Reproduce It First
Reproducing a bug does not mean replicating the code. It means making the bug happen again on purpose, consistently.
If your app crashes when a user submits a form with an empty field, reproducing it means you can make it crash every single time by submitting that empty field. You control it now.
Why this matters: if you cannot make it happen on purpose, you are fixing blind. You might change something, the bug disappears, and you have no idea if you actually fixed it or just got lucky. When you can reproduce it, you know exactly when you have solved it because it stops happening and you understand why.
If you cannot reproduce it consistently, you do not fully understand it yet. Do not touch the code until you can.
Share
Step 3: Follow the Stack Trace Downward
This is the one nobody explains properly.
A stack trace is a list of every function call that was active when the crash happened. The most recent call is at the top. Where it all started is at the bottom.
TypeError: Cannot read properties of undefined (reading 'map')
at ProductList (components/ProductList.jsx:23:14) <- where it crashed
at renderWithHooks (react-dom.development.js:14985:18)
at mountIndeterminateComponent (react-dom.development.js:17811:13)
at HomePage (pages/index.jsx:45:10) <- where it started
at App (pages/_app.jsx:12:8)
Here is how to read it:
Ignore the framework noise. Any line referencing react-dom, node_modules, webpack, or vendor is not your code. Skip it entirely.
Find your files in the trace. Look for paths you recognize. In the example above, that is ProductList.jsx and pages/index.jsx. Those are yours.
Start at the bottom of your code, not the top. The bottom is where the chain of events started. The top is just where things finally gave up. So, HomePage at line 45 triggered something that eventually broke in ProductList at line 23.
Ask what was passed in. Go to that bottom file, find that line, and ask what data arrived there. Nine times out of ten, the bug is in the data, not the logic. Something was undefined, empty, or the wrong shape before it even reached the crash point.
The error shows up in one place. The cause almost always lives somewhere else.
Step 4: Isolate, Don’t Guess
When you do not know where a bug lives, the instinct is to look everywhere at once. Isolation is the opposite. You shrink the problem until it has nowhere to hide.
Divide and conquer
Split your code in half mentally. Is the bug happening before this point or after? Once you know, split that half again. Keep halving until you have cornered it.
Example in a form submission flow:
User fills form → data hits handler → API call made → response comes back → UI updates
Drop a console.log after each step. Where does the data look wrong for the first time? Everything before that is fine. Your bug lives after the last clean log.
Comment out until it works
Remove pieces until the bug stops happening. The last thing you removed is either the bug or pointing directly at it. You are not fixing yet. You are locating.
The rubber duck method
Explain your code out loud, line by line, to anything. Your dog, a coffee mug, an empty chair. It does not matter.
The act of explaining forces your brain to slow down and be precise. You will catch yourself saying, “and then it does... wait, that is wrong” almost every time. Your brain skips over familiar code when reading. It cannot do that when speaking.
Step 5: Fix the Cause, Not the Symptom
This is the big one. And it is where a lot of developers stay stuck without realizing it.
A symptom fix makes the error go away. A cause fix makes the problem go away. They look identical on the surface and feel completely different six weeks later.
Classic example. Your app crashes because a value is null. The symptom fix:
javascript
if (!user) return null
That stops the crash. But you never asked why the user is null. Is the API not returning it? Is auth failing silently? Is there a race condition?
The cause fix means tracing back to where the user should have been set and figuring out why it was not. It takes longer in the moment and saves you hours later.
How to know if you are fixing a symptom:
Ask yourself honestly: do I understand why this broke, or am I just stopping the error from showing?
If you cannot explain the why in one clear sentence, you are probably treating a symptom.
A codebase full of this looks like:
javascript
user?.profile?.settings?.theme ?? 'default'
Optional chaining everywhere is not defensive coding. It is a codebase full of unresolved questions wrapped in duct tape. At FabBuilds, when we inherit a project like that, every one of those chains is a flag that something upstream was never properly handled.
The Full Process
Read the error before Googling it
Reproduce it until you can make it happen on purpose
Follow the stack trace downward to find where it started
Isolate the bug by dividing, removing, and explaining out loud
Fix the cause, not just the thing that is showing red
None of this was taught in my university courses. I learned it the hard way, jumping into client codebases at FabBuilds, inheriting other people’s decisions, and being forced to understand things before touching them.
The good news is that once this process becomes a habit, debugging stops feeling like panic and starts feeling like problem-solving. That is the whole difference.
Ready to build something the right way from the start? fabbuilds.org
Let’s Build It Beautifully,
Fab