Let's take a look at three different strategies we can employ when debugging code.
Code that is executing in a browser or on a device generally outputs some information to the console. You can easily see this by opening up Developer Tools in Chrome or the Web Console in Firefox. When something goes wrong in your application, you can look for error messages in the console. Helpful error messages like "The file 'address.js' was not found" can tell you exactly what's going wrong.
Often an error in an application will produce a stack trace. A stack trace is simply a series of error statements that go in order from the most recent file that was called all the way back to the first file that was called. Here's a very simple example: let's say that you have a Node application that displays cat photos. Your main app.js page calls a function called getCats which will load the user's cat photos. But something goes wrong with getCats, and the application crashes. Your stack trace might look something like this:
Error: cannot find photos
at getCats.js 10:57
at app.js 15:16
- The first line of the stack trace is the error- the main cause of what went wrong.
- The next line shows the last thing that happened before the app crashed: the code was executing in getCats.js, and when it got to line 10, column 57, it couldn't find the photos.
- The third line shows which file called getCats.js: it was app.js, and it called getCats at line 15, column 16.
- The final line shows what file was called to run app.js in the first place: an internal Node file that called app.js at line 17, column 47.
Stack traces are often longer, harder to read, and more complicated than this example, but the more you practice looking at them, the better you will get at finding the most important information.
Much of what you see in the console output can be called logging, but there are often specific log entries set up in an application's code that record everything that happens in the application. I'm fortunate to work with great developers who are adept at creating clear log statements that make it easy to figure out what happened when things go wrong.
Log statements often come with different levels of importance, such as Error, Warning, Info, and Debug. An application can sometimes be set to only log certain levels of statement. For example, a Production version of an application might be set to only log Errors and Warnings. When you're investigating a bug, it may be possible to increase the verbosity of the logs so you can see the Info and Debug statements as well.
You can also make your own log statements, simply by writing code that will output information to the console. I do this when I'm checking to make sure that my automation code is working like I'm expecting it to. For example, if I had a do-while statement like this:
while (counter < 10)
I might add a logging statement that tells me the value of counter as my program progresses:
console.log ("The value of counter right now is: " + counter)
while (counter < 10)
The great thing about creating your own log statements is that you can set them up in a way that makes the most sense to you.
A breakpoint is a place that you set in the code that will cause the program to pause. Software often executes very quickly and it can be hard to figure out what's happening as you're flying through the lines of code. When you set a breakpoint, you can take a look at exactly what all your variable values are at that point in the program. You can also step through the code slowly to see what happens at each line.
Debuggers are generally available in any language you can write code in. Here are some examples:
- Python uses the pdb library
- Powershell uses breakpoints in the Powershell ISE
- C# has all kinds of debugging tools in Visual Studio
- Java has debugging tools in Eclipse and IntelliJ
I hope this post helps you get started with both debugging your code, and investigating someone else's bugs!