This blog was original posted on Keyhole Software’s blog on 2/10/2014.
Introduction
Writing Part 2 of this series has been really exciting. I’ve gotten a lot of positive feedback from people who’ve read Part 1. This post will be more hands-on than the previous one, so be ready for screenshots and real case scenarios I’ve come across while working with JavaScript.
I’m also excited to announce that I’ll be presenting at KCDC this year! It will very similar to this series, except with an Agile twist. Stay tuned for information on what day and time.
Review of Part 1
In my previous post on advanced debugging techniques in JavaScript, I spelled out the idea of validating assumptions. This is the most important concept when it comes to debugging, and is the foundation for every other aspect of debugging I will discuss. I then described the binary search technique, which can be applied to any language, as well as the debugger statement, which is specific to JavaScript.
Overview of Part 2
In Part 2, I will describe two techniques that can be applied to debugging any programming language: bottom-up and top-down debugging. It is very important to understand bottom-up and top-down debugging before diving into different ways they can be used. I will also go into the mechanisms JavaScript has to implement these types of debugging.
Bottom-Up Debugging
Even though most people aren’t familiar with the term, bottom-up debugging is the first debugging technique most programmers are taught. Bottom-up debugging boils bugs down to a cause and effect relationship. The effect of the bug is often obvious, like sales tax on an accounting application is incorrectly calculated, but the cause is almost always obscure. Finding the cause is done by starting to debug the program at a place where it is correct, then looking at the effect of each method call to determine if it is the culprit.
Bottom-up debugging is one of my least favorite techniques. I would only use or recommend it when all other techniques have failed. From experience, bottom-up debugging is tough for the following reasons:
- The place where the developer starts debugging may be thousands of method calls away from the cause of the bug.
- The flow of control may be asynchronous or disjointed, so following step by step can be problematic.
- The place where the developer starts debugging may be in a completely different part of the the code than the bug.
- It can allow developers to spin their wheels without getting closer to fixing the bug.
If bottom-up debugging is used, then it’s necessary to quickly eliminate large sections of code when hunting for the cause of the bug. It’s very easy in bottom-up debugging to step into every method. This is acceptable when code is well isolated and small, but can be counter-productive with a large code set.
Let's say I’m debugging the issue described previously where the sales tax isn’t correctly calculated. I decide to use bottom-up debugging, and choose the getSalesTax
method of my Balance
class. It looks like the following:
Balance.getSalesTax = function () {
var balance = this.getBalance();
var currentBalance = balance.getCurrentBalance();
var taxInfo = this.getTaxInfo();
var salesTaxRate, salesTax;
if (!taxInfo.hasSalesTaxRate()) {
salesTaxRate = taxInfo.getSalesTaxRate();
}
else {
salesTaxRate = this.getDefaultSalesTaxRate();
}
salesTax = currentBalance * salesTaxRate;
return salesTax;
}
Rather than stepping into the getBalance()
method, I would start by stepping over it. I would validate that the method returns exactly what I was expecting it to return. If it does, then I know either my assumption is wrong or the bug is later on in the code. I would repeat this process for every part of the method until I find a place where an assumption fails or I find the bug.
The issue may be with one of the other methods returning an incorrect value. If this method returns the value as expected, then the cause of the bug may be later on in the process. Eliminating this method as the likely cause of the bug is possible by taking this approach.
Bottom-up debugging can be frustrating if the developer starts debugging far away from the cause of the bug. I’ve had situations where dozens and dozens of different start point yielded no results. The best thing I took from those cases was to keep refining my approach until I got better at this technique. Bottom-up debugging is a good last resort when all else fails as long as the starting point is chosen well.
Top-Down Debugging
As it should be apparent from the name, top-down debugging is like flipping bottom-up on its head. Bottom-up debugging works well when the effect is well known but the starting point is not obvious. Top-down debugging focuses on starting with the effect, then working backwards to find the cause.
I’ve found this approach to isolate the causes of bugs more quickly than bottom-up. I’ve found that when many people work on the same code base, the cause of bugs tends to be very close to its effect.
The hardest part in bottom-up debugging JavaScript applications tends to be finding the best place to observe the effect. Unlike languages like C++ and Java, it is very difficult in JavaScript and DOM to hook into specific points when things change.
A good understanding of Chrome’s Developer Tools are essential to do effective top-down debugging. I will go through some features in Dev Tools that allows the developer to observe the JavaScript that caused the page to change.
DOM – Break on…
Lets say I’m a new Front End developer of http://www.php.net. I’m tasked with figuring out why some of the Flickr photos at the bottom show up weird. This is what the page looks like:
Because I’m a new developer, I may feel overwhelmed because of the size of the PHP.NET code base. I need some way to quickly go to the JavaScript code that is inserting those images. Chrome’s Developer Tools are perfect for this task.
The first thing I would do is to press F12 to open up Dev Tools. I would then click the Inspector, then click on the first image. The screen should look like:
Notice how the magnifying glass next to the Elements tab is blue. This is what I clicked on to activate Inspect. I then clicked on the first image, which is highlighted in blue on the left and selected on the right.
The DOM structure looks pretty simple: a DIV
whose class is “elephants
” has a DIV
whose class is “images
” has a list of A tags with the image. If I want to know where those A tags are inserted, I could set a listener though Dev Tools on the images’ DIV
. To do this, you right click on the tag, scroll down to Break on…, and select Subtree Modification.
The three different options in Break on… allow your debugger to be activated when various DOM changes occur. I chose Subtree Modifications because the JavaScript should add elements to the images DIV
. If attributes or styles were added to the DIV
itself, Attribute modifications would be the ones to select. If our bug related to images being incorrectly removed, then Node removal is best.
After setting the break, the page needs to be refreshed. This is the JavaScript I see when it runs again:
There are many different things to look at on this interface. It’s easy to get lost in all of the different pieces that Dev Tools offers. It looks like they’re using jQuery to implement UI changes. Most websites use a JavaScript framework to accomplish basic functions.
One common approach at this point is to look at each method call up to this point. Checking that the arguments are correct is usually the first check. If the method before this one had incorrect arguments but the one before that was correct, it’s possible the method before this one is the cause of the bug. This idea can be extended to each method in the call stack.
A slightly better approach would be to look at where the methods in the call stack are coming from. The top three come from jquery.js. It’s possible that the framework the page is using has a bug, but the more probable situation is that the code written for PHP.NET uses jQuery.
The first thing I look for in this situation is the top-most method call on the stack that comes from a custom file written for the website. In this case, cached.php is where the $.ajax.success
callback is defined. If there is an issue, I’ve found that the first custom method is likely be the culprit.
This style of debugging differs from bottom-up in that it focuses on getting as close to the effect as possible. If bottom-up debugging was chosen, finding the starting point for a new developer would be extremely difficult. Using the tools given to the developer by the browser, an intelligent, clear point could be chosen to start the debugging.
Conclusion
The two debugging techniques I discussed in this blog are bottom-up and top-down. Bottom-up debugging is best used as a last resort when the effect of a bug is difficult to isolate. Top-down debugging works well when it’s easy to break on the effect of the bug. Both of these techniques can be used outside of JavaScript. While in Chrome, I showed how Dev Tools allow breaking on DOM modifications. I will show other ways to implement top-down debugging in future parts.
The post Advanced Debugging Techniques In JavaScript – Part 2 appeared first on Zach Gardner's Blog.