There are a lot of strategies to debug the problem in your code. From my experience there is no a silver bullet to manage all the situations with only one strategy. Generally I found three main strategies ranged by common usage from the most to the least popularity.

Brute Force method. This method is the most common and least efficient. Here developer just prints out all relevant memory stacks and observes the potential place of inconsistency with expected data. Back Tracking method. This approach is good in a quite small applications. The process starts from the place where a particular symptom gets detected. From there backward tracing is done across the entire source code. The steps here: observing who was a caller for a particular function, getting to its caller, checking if problem related to that function appeared there. If not, going to the caller of that function, and so on. Cause Elimination method. This approach is used quite rarely. It’s also called induction and deduction. Data related to the error occurrence are organized to isolate potential causes. Developer create a hypothesis of a bug cause and compose a special data shape to pass into function which should prove or disprove that hypothesis. Own experience method. It needs a good knowledge of the product and its potential weak places. In such case developer already knows what could be the reason of a reported bug based on knowledge that API could unexpectedly be changed and source code is sensitive to such change. Or data type was changed but source code doesn’t have an appropriate fail check for it.

Let’s overview some practical methods of debug process.

Simple debugging approach with logging output

In such approach developer just adds print outs into the code (console.log, alert, etc). This helps to understand some intermediate states related to the code. This approach stays somewhere in the middle between Brute Force and Back Tracking methods (closer to a Brute Force however). In the system where in development mode there are already tons of logging messages, it becomes quite painful to add extra logs and match these logs with a concrete place in the code. And, of course, all this information should be kept in developers mind and there is high chance to shift focus from problem solving to remembering console.logs placement and getting your mental stack overflow at the end. And in such case developer should already rely on source-mapping tools to not deal with minified code.

Debugging with REPL

REPL stands for Read Eval Print Loop. Such tools represent a computer environment like a shell where a command could be entered and the system responds with an output in an interactive mode. If developer has an access to the source code module it’s easy to build a Cause Elimination method strategy. For example developer already located the potential place of the buggy code and with REPL can pass a hypothesis data to prove that it’s a correct bug location.

REPL for node.js

Debugging with Developer Tools

Developer Tools is great place to get a verbose feedback from the system. For example Chrome Developer Tools helps to debug not only JS code itself, but network calls, memory heaps, closures, put breakpoints on interested function call, debug browser DOM and css calculations related to it.

But mainly I want to stay on pausing code with breakpoints functionality. It’s a perfect example of Back Tracking method strategy. It helps to stop the code in an interested place, observe its call stack (and navigate to a direct on indirect callers), inspect them and so on.