DEV Community

Cover image for Guide To Effective JavaScript Debugging
Essomia
Essomia

Posted on

Guide To Effective JavaScript Debugging

One thing is for sure, any experienced developer would say that debugging is a skill that consumes at least half of our time.

We develop features, optimize old code, fix old problems, but every time we touch our code base, it can lead to a new bug. And even if we are careful, and we have many safety nets (code review, pair programming, ...), a bug can happen at any time.

And once a bug occurs and the client contacts the team in panic, the stress begins! How to fix it? Where do we start? How much time do we need? How do we find the sources of the problem?

So today, I want to share some personal tips and resources that you must know for debugging JavaScript, in hope that will help master your best ally: the DevTools Console!

Table of contents


The Process

So, what is debugging?

"Debugging" is the act of finding the source of a problem in a code base, identifying possible causes, testing hypotheses until you find the ultimate root cause. And then eventually eliminate that cause and ensure it never happens again.

Most developers think that debugging means fixing the problem, but it actually means finding the sources of the problem.
Sometimes clients are okay with a minor bug as long as it doesn't affect their business.

But one thing is for sure, debugging is a skill that can't be taught in school... but it can be mastered through practice.

Fundamental steps to debugging

As a reminder, sometimes we forget the basic debugging steps, because all we want is to dive into the code to solve the problem as soon as possible!

  1. Reproduce - Try to reproduce the error from the information provided. If you can't see the error, you'll never be able to fix it! And most important: Make sure it's a real bug!
  2. Analyze - Think about the possible causes and identify at least two or three good hypotheses that you can test. Try to understand how your application works to find the location of the error. Here, it is important to know what you are looking for and what to look for.
  3. Test - Break down your test steps into small parts and modify your code one line at a time to identify the exact source of the problem. We also need to confirm if the error is not one of the symptoms of a larger underlying problem that needs to address.
  4. Fix and validate - If the client is ok with the solution, implement it! And validate all possible scenarios to prevent the problem from coming back. A good way is to write unit tests or keep a list of known bugs/problems.

And don't forget: Problems don't go away by themselves! If you can't figure out how the error was solved, it means you're not done yet.


Tools for debugging Javascript

For debugging, you have three big part you can use: error logging, sources debugger and sources mapping. I will explain them going from the basic debugging to the more advanced concept.

Alert

I wanted to start with an important point: never use alert() to debug your code! If you've ever used it, you should know that it's a nightmare in a loop or when debugging a JavaScript application that re-renders your component, that a big no!

Console API

The Console API contains many methods developed to facilitate rudimentary debugging.

I share below my most used methods, but I invite you to consult the documentation to check the others: Web API Console (on MDN)

  • console.log(var1, var2);
    • For a general output of the variables, without context.
    • Tip: Use with {} (ex: console.log({ var1, var2 });) for a display with the variable context.
  • console.table(array);
    • To display the data of an array/object in a table.
    • Tip: Once the table is in your console, you can order the columns by clicking on them.
  • console.dir(domElement);
    • List the specific properties of a JavaScript object to examine its contents.
  • console.count(var1);
    • To count the number of time the line is rendered.
    • Tip: Use it to know how many times a callback or a component is called.
  • console.trace();
    • Display the stack trace to better understand the order of invocation of your code.

The debugger statement

One thing that is sometimes overlooked is the "debugger" statement. When written in your code, it will automatically start the debugger of your browser (if it is open) and act as a breakpoint.

Did You Know? When you hit a breakpoint, the console can access to every variable available from the scope you are currently in.

Breakpoints : types and usage

You have three kinds of breakpoints available:

  • Line Of Code: Will pause your code, when the script has reached your breaking point.
  • Conditional Line Of Code: Will pause your code, only if a certain condition is met.
    • Tips: Use it if you need to pause your code for a specific product in a loop (ex: data.length > 1, id = 12).
  • Log Point: Will NOT pause your code, but allow you to see the content of a specific value.
    • Tips: The alternative to console.log that doesn’t require modifying/compiling any code.

When your debugger hit a breakpoint, your code will be paused. Now you can navigate through your code with some main stepping options:

stepping in chrome

  • Resume: Continue the script execution until it runs into another breakpoint.
  • Step Over: Continue the script execution until the next functions after your breakpoint, then pause again.
  • Step In & Step Out: Continue the script execution and goes in/out your current functions, then pause again.
  • Deactivate: Disable all breakpoints to be able to execute all the scripts without pause.
  • Pause on Exception: Run all your script, but pause if an error is thrown in a try-catch.

Here what is important to understand: stepping is from functions to functions and not from line of code to line of code.

Did You Know? Browser proposes a lot of breakpoints based on specific interactions (such as events listeners, XHR, DOM change…) that will help you pause your code for a specific event. You can also add a JavaScript expression to your watch list to monitor any change happening while your scripts is running.

Call Stack

Reading the call stack is really helpfull since it is the stack trace from your current breakpoint to the very first call in your app/page. stack trace helps you understand the order of invocation of your code.

Did You Know? Browsers allows you to add some files to ignore list (blackboxing). Once ignored, a file will not be stopped while stepping in the stack strace, allowing you to focus on the code you can manage. Really useful to ignore libraries and node modules.

Sources Maps

If you follow the best practices for performance, you will compile and minify your sources' files before pushing your application code into production. But minified codes are pretty much impossible to debug properly.

Sources maps are often disabled on production, where, in fact, that the place where you really should activate them. That the place that we want to know what's going on.

So, always be sure to enable source maps for your JavaScript by setting up the correct configuration:

## an example in webpack.config.js

## -- for development --
mode: development
devtool : 'inline-source-map'

## -- for production --
mode: production
devtool : 'source-map' ## enable browser sourcemaps for production
output: {
   path: path.resolve(__dirname, './dist'),
   filename: '[name].js',
   sourceMapFilename: '[name].js.map',
},
plugins: [
   ## Be sure to check your plugin configuration if they have sourceMap specific configuration
   new UglifyJsPlugin({ sourceMap: true })
],
Enter fullscreen mode Exit fullscreen mode

Did You Know? Source Maps are only loaded if you open the DevTools.

Also, don’t forget that security through obscurity is never a good idea!
If you have some concern over security from pushing your sourcemaps on your production's environment, you can add a rule to your production website to allow only people behind a specific IP to access theses .map files. You can also modify the URL where the sources map will be hosted with SourceMapDevToolPlugin to target another server.

Local Overrides (only on Chrome)

Local Overrides are the best option when you want to experiment without mapping your changes with a local source.

When the feature is enabled, if you save any file, it will appear in the overrides' panel with a purple dot and a copy will be saved to a local folder. Now, Chrome will ignore the code coming from the server for the overridden file and will instead execute the code you have saved.

The downside is whenever your code is ready, you will need to manually merge the file from your saved local folder with your project’s source code.

Did You Know? The changes made and saved in Local Overrides will persist across page refreshes and browser restarts.

Workspaces/Filesystem (only on Chrome)

Workspaces feature allows you to directly make changes to your local project’s sources code from Chrome DevTools. It’s like using chrome DevTools as an IDE.

Once a Workspace is set up, the JavaScript file being served from the server will be overridden by your local source file.

Did You Know? If you set up correctly workspace filesystem with source map, you can easily debug a production website and test your fix right away.

Proxy Overrides

A Proxy Overrides will redirect any URL request on the fly before requests are send to the server. These tools are great for debugging production websites, as you can edit and inject code on the fly. You no longer have to scan through minified code, as you can just redirect to an unminified version.

I won't go into details here, but in case you need an advanced system to debug network requests and responses, know that Proxy Overrides exists.


I know, debugging is not easy! Each bug is different from another. But now, you should have a good overview of what the tools at your disposal can do for you. Integrate them into your debugging habits and your development speed will increase exponentially!

Top comments (0)