Most developers debug by adding console.log statements everywhere and staring at the output. That works sometimes. Here's what works every time.
Step 1: Reproduce it
If you can't reproduce a bug, you can't fix it. Before touching any code, write down the exact steps to trigger the issue. "It's broken" is not a reproduction. "Click the submit button with an empty email field on the signup form in Chrome" is.
If a bug is intermittent, that's information too. It means there's a race condition, a timing issue, or an environmental dependency. Knowing that narrows the search immediately.
Step 2: Form a hypothesis
Before writing any debugging code, think. Based on the symptoms, where could the problem be? Form a theory. "The API returns 500 because the database query fails when the name field contains an apostrophe." That's testable.
The hypothesis doesn't need to be right. It needs to be specific and falsifiable. If you're wrong, you've still learned something about the system.
Step 3: Narrow the scope
Binary search the problem. If the bug is somewhere between the frontend and the database, check the API response first. Is it correct? If yes, the bug is in the frontend. If no, check the database query. Is it correct? Keep halving the search space.
This is faster than reading every line of code from top to bottom. In a codebase of 10,000 lines, binary search finds the bug in ~14 steps. Sequential search takes 10,000.
Step 4: Read the error message
This sounds obvious, but I've watched experienced developers spend hours debugging something when the error message told them exactly what was wrong. Read the full stack trace. Google the exact error message. Check if someone else hit the same issue on the library's GitHub.
The answer is in the error message more often than you'd think.
Step 5: Check what changed
If something worked yesterday and doesn't work today, something changed. git diff is your best friend. Check recent commits, dependency updates, environment changes, config changes. The bug is probably in the delta.
Step 6: Rubber duck it
Explain the bug to someone — or something. Out loud. Walk through the code path step by step and explain what each line does. The act of explaining often reveals the assumption you're making that isn't true.
Most bugs aren't caused by code doing the wrong thing. They're caused by code doing exactly what you told it to do, based on an assumption that's incorrect.
The meta-skill
Debugging isn't about tools or techniques. It's about staying calm and being systematic. The best debuggers I know aren't the smartest engineers — they're the most patient ones. They don't panic, they don't make random changes hoping something sticks, and they don't skip steps.
Be methodical. Be patient. The bug isn't hiding from you. You just haven't found it yet.