The Rules of Debugging
The bug makes no sense.
You've checked the code. You've checked the database. The output is still wrong. You start questioning reality. Maybe it's the compiler. Maybe it's the framework. Maybe the cloud provider is lying to you.
This is the panic phase. Everyone hits it. And it's almost always unproductive, because it skips the discipline that actually finds bugs.
Assume nothing. You know what that variable is. Print it anyway. Log it. Inspect it. Reality often diverges from the code you think you wrote, and the gap between what you assume and what's actually happening is where most bugs hide.
The problem is probably you. Not the compiler. Not the framework. It's usually a typo, a race condition, or a misunderstanding about how a library works. Approaching debugging with humility instead of suspicion gets you to the answer faster.
Simplify. If you can't find the bug, the system is too large. Cut it down. Can you reproduce the error in ten lines of code? If not, keep deleting until the bug either disappears — telling you exactly what caused it — or becomes obvious.
Talk to the duck. Explain your logic out loud, line by line, to an inanimate object or a patient teammate. The goal isn't the other person's answer — it's the act of articulating each step carefully enough that you can't skip over the parts you "obviously" understand. Bugs tend to live in exactly those skipped-over places.
That feeling of "this shouldn't be happening" is just the gap between your mental model and the system's actual state. Debugging is the process of closing that gap — not through intuition, but through elimination. Narrow the possibilities systematically, and the truth emerges on its own.