Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

The Five Stages of Debugging (and the Method That Skips Most of Them)

About Post

A bug report arrives. "The invoice total is wrong for some customers." Not all customers. Some. On some days.

You know what happens next. Not because you've read about it, but because you've lived it, probably more than once this year. Psychologists describe five stages of grief. Developers have their own version, and we go through it with every bug that matters.

Stage 1: Denial

❌ "That's impossible. The code doesn't do that."

You open the function. You read it. It is, as far as you can tell, correct. You ask for a screenshot, quietly hoping the user is looking at the wrong page. They are not. You check whether it's maybe a different environment. It's production.

The code is doing exactly what it was written to do. You just haven't accepted what that is yet.

Stage 2: Blame

❌ "It must be the cache."

Then the framework. Then the browser. Then that third-party API. Then the last person who touched this file, until git blame politely reveals it was you, eight months ago, with the commit message "small fix".

To be fair, sometimes it really is the cache. That's what keeps this stage alive.

Stage 3: Bargaining

❌ "If I clear everything and restart, it'll go away."

This is the stage of random acts of hope. php artisan optimize:clear. Restart the queue workers. Delete node_modules and reinstall, just in case, even though this is a PHP bug. Add dd() in six places. Add a console.log('HERE 2'). Change something unrelated "to see what happens".

Occasionally the bug disappears during bargaining. This is the worst outcome, because it'll be back on Thursday and you'll have no idea why it left.

Stage 4: Despair

❌ "Maybe I should rewrite the whole module."

It's late. You've read the same forty lines so many times they've stopped meaning anything. You start to doubt basic arithmetic. You open the AI assistant and paste in the function with the message "why is this wrong", and it confidently suggests a fix for a different bug.

This is also the stage where people go for a walk and solve it in the shower. Remember that.

Stage 5: Acceptance

✅ "Oh. Oh no. It's the time zone."

The cause is found, and it's always smaller than the suffering. A < that should be <=. A date calculated in UTC and displayed in local time, so invoices created just after midnight land in the wrong month. A missing index on one join. The fix is one line. Sometimes one character.

And then, the bonus stage every developer knows: "How did this ever work?"

The method that skips most of the stages

The stages are funny because they're true, but they're also optional. Most of the pain comes from debugging by mood instead of by method. Here's the calm version I try to follow, especially when it's urgent.

1. Reproduce it before you touch anything

Find the exact input that breaks it: which customer, which date, which data. "Some customers on some days" is a pattern you haven't found yet. A bug you can reproduce on demand is half fixed; a bug you can't reproduce isn't ready to be fixed.

2. Read the whole error, and the logs around it

Not just the first line. The stack trace, the line numbers, the previous exception, the log entries just before it. A surprising number of bugs are fully explained by a message nobody scrolled down to read.

3. Write down one hypothesis, then test it

"I think invoices created after 21:00 local time are counted in the next day." Now you can prove or disprove that with one query. Bargaining is changing things and hoping; debugging is changing one thing and predicting what will happen.

4. Binary search the problem

If it worked last month, let Git find the commit that broke it. git bisect halves the search space every step:

git bisect start
git bisect bad                 # current commit is broken
git bisect good v2.3.0         # this release was fine
git bisect run php artisan test --filter=InvoiceTotalTest
git bisect reset

Write a small failing test first, and bisect run will find the guilty commit while you make tea. The same halving trick works without Git too: comment out half the pipeline, check the data halfway through, and narrow it down.

5. Explain it out loud

To a colleague, a rubber duck or an AI assistant. The trick isn't the listener; it's that explaining forces you to state assumptions you've been skipping. AI tools are genuinely useful here if you give them the reproduction, the error and your hypothesis, not just "why is this wrong".

6. Fix it, then lock it in

Keep the failing test from step 4. Now it passes, and it will fail loudly if anyone brings the bug back. That test is the real deliverable; the one-line fix is just the receipt.

The rule: if you've changed three things and don't know which one mattered, you're bargaining, not debugging. Undo, reproduce, and change one thing at a time.

So, where are you right now?

Every developer has a home stage. Some of us live in Blame ("it's definitely the cache"); some go straight to Despair and start planning the rewrite. Which stage do you spend the most time in, and what finally pulls you out of it?

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close