Are you one of those who are console logging your way to a solution when your code is not behaving the way you expect? If yes, read on.
By the end of this article, you’ll be able to debug your code better and faster than ever before.
This is a summary of what we’re going to go through:
If the cause you’re debugging your code is due to a bug or unexpected behavior, you’re likely interested in the “Sources” tab in the Dev Tools, which is the section we’re going to explore in depth through a set of various scenarios.
If you are reading this article, you probably are used to debugging a particular line by logging to the console a certain value. But I want you to introduce to a more powerful way that goes way deeper in the code than simply logging: breakpoints.
Setting breakpoints is normally the first step of the debugging process. The built-in development tools in most browsers allow you to stop the execution of your code at a particular line of code and at a particular statement on every line of code running on the page being debugged, but for the purpose of this article, we will be specifically using the Chrome Dev Tools.
Normally, you may want to stop the execution of the code so that you can interactively inspect the particular context that we’re interested in.
Once the code has stopped at the breakpoint, we can start the debugging process by getting access to the scope, navigating the call stack, and even change the code at runtime.
In order to explain how to set breakpoints, we will debug an Angular codebase of one of the side projects I use for education purposes, although the technology used doesn’t really matter.
As you can see in the image above, we can go deeper than setting a breakpoint on a line of code, and we can also set it to the statements on the same line of code.
We set 3 breakpoints:
priceReceivedgets called, so we can also inspect the return value of the arrow function
When the arrow function gets called, the execution stops and the right-hand panel
Scope gets populated with information regarding the current context, and gives us access to all the scope so we can inspect the values we’re interested in.
In this case, you can see how we can see the value of the variable
In the image below, our third breakpoint gets hit once the function
priceReceived has been executed.
As you can see in the right-hand panel,
Return value will show us what the anonymous function returns
Scenario: you set a bunch of breakpoints all over the codebase.
As you may have experienced, it’s very common to refresh the page multiple times while debugging.
The code you’re currently debugging may have various breakpoints and sometimes, these can even be called hundreds of times! Yeah, it can be frustrating and time-consuming.
In such cases, I recommend to temporarily pause the execution of all breakpoints, and you can do this by toggling the icon you can see in the image below:
Scenario: you get an unexpected error, but you don’t want to set a breakpoint as you’re unsure when exactly the error is going to be thrown.
You can stop the execution as soon as an error is thrown, so you can inspect the scope and understand what went wrong.
Conditional Breakpoints, as the name suggests, allow us to only trigger certain breakpoints if a condition is truthy.
For instance, in the example shown above, the user can input in the text area non-numerical values. JS is very forgiving and will just display
NaN instead of throwing an error.
Scenario: you have more complex code than the one above, and can’t figure out when the result is
Of course, you could set a breakpoint, but it’s not easy to reproduce the error and you end up spending half an hour stepping through the execution of your code. This is a scenario where you could use a conditional breakpoint and break the execution only when the value inspected is
See the image below:
In order to fully take advantage of the Dev Tools, it’s worth spending a little bit of time and learn how the Dev Tools helps us to quickly step through the code without setting breakpoints at each line.
If you pay attention to the image above, multiplyBy and renderToDOM were executed but the debugger did not step into them like the previous navigator did (Step).
Watch the image above: chronologically, line 32 should have been run, but it didn’t. The debugger waited and moved to line 29 after 2 seconds
What happened in the image above?
Sometimes it can be useful to store in the global scope some values such as a component’s class, huge arrays or complex objects.
Adding these values to the global scope while debugging can be a huge time-saver when you want to, for instance, call a method on that component with different parameters.
In the image above, I save the array
[previous, current] as a global variable. The Dev Tools automatically assign a name to the variable, which is
n based on the number of previously saved variables.
As you can see in the image above, the variable gets named
temp2 and I can use it in the console as it is now defined globally!
Thanks to Eager Evaluation, a feature released in Chrome 68, the Dev Tools allows the evaluation of statements in the console as you write them and also displays the signature of the method.
If you pay attention to the image above, when I map the saved variable to an array of strings, the result is immediately visible without me having to press Enter.
Navigating the call stack is one of the most useful tools that the Dev Tools provide: not only can you jump back and forth in the call stack, but you can also inspect the scope at each step.
Assume we have a dead simple page and a script that takes in input a number and will render on the page the number multiplied by 10. We will call two functions: one to multiply, and one to render the result to the DOM.
As you can see in the image above, we are able to navigate through the call stack just by clicking on the name of the function in the pane “Call Stack”.
As you may have also noticed, every time we jump from a call to another, the scope is retained and we can analyze it at each step!
Blackboxing scripts will help declutter the call stack by excluding from the stack certain scripts or scripts that match a certain pattern.
For instance, if I am solely interested in debugging the userland code, which is probably 99% of the times, I will add a pattern to black box all the scripts under the folder
In order to black box a script, you have two ways:
For example, you can write an expression and expect the result of this expression to always be
true so that when the expression will be
false , you know something is wrong in the current state.
There’s a catch:
The Dev Tools are an incredible resource for debugging complex code. Sometimes you may dig deeper than console logging, and the tools above will allow for an in-depth debugging experience. These tools require a little practice before you’ll be fully comfortable using them, so don’t be put down if you’re feeling unfamiliar with all the options available.
Here are some resources to fully understand all the available options the Dev Tools provide: