Systematic debugging is a loop: observe what actually happens, form one specific hypothesis, test only that hypothesis, and narrow. The random-changes approach fails because you're altering multiple variables at once, so even a success teaches you nothing.
The method, in order:
1. Reproduce it reliably. If you can't make the bug happen on demand, you can't tell whether you fixed it. Find the exact steps first — this step alone often reveals the cause.
2. Read the error and the stack trace properly. Say the message out loud. The top frames of the trace are usually your code; that's the entry point.
3. State what you *expected* versus what *happened*. Bugs live in the gap, and being precise about the gap ('I expected an array of five, I got undefined') usually points straight at the layer to inspect.
4. Bisect the path. Your code goes A → B → C → D and the output is wrong. Check the value at the halfway point. Correct there? The problem is in the second half. Wrong? First half. Repeat. This halves the search space each time and finds the culprit in a few steps regardless of codebase size — it's the single most valuable debugging technique and almost nobody is taught it.
5. Print or breakpoint at the boundary. `console.log` the value just before and just after the suspicious operation. Learn your debugger too — stepping through with a breakpoint and watching variables change is much richer than print statements, and most people never invest the hour it takes to learn.
6. Change one thing at a time, and undo it if it didn't help. Otherwise you accumulate a pile of half-changes and a codebase you no longer understand.
The assumptions worth checking early, because they cause a startling share of bugs:
- Is this code even running? Put a log at the top. Frequently the answer is no, and you've been debugging the wrong function.
- Is the data what you think it is? Log the whole object, not the property. Types, nesting and casing are constantly not what people assume.
- Did you save the file? Is the server restarted? Is the browser showing a cached version?
And rubber-duck it: explain the problem line by line, out loud, to a person, a duck, or a text box. Articulating forces you to check the assumptions you were skipping, and a large fraction of bugs are found mid-sentence before anyone answers.
The mindset that changes everything: treat a bug as information rather than an insult. The program is doing exactly what you told it to; your job is to find where your instructions differ from your intent. That framing turns debugging from a frustrating interruption into the most instructive part of the work.