A (very) Brief Primer on Debugging in Software Development

As a software developer, one of my constant goals when writing code is to ensure that I’m not introducing problems into a program or application. Problems in programs or applications are often referred to as “bugs,” a term with which you might be familiar. So, how do software developers go about checking their code for bugs, identifying existing bugs and fixing those bugs? The general term for this activity is called “debugging,” and while the term itself may have some negative connotation, it’s an essential part of developing and maintaining reliable software. So, for today’s post, we’ll broadly examine the topic of debugging through its history and some common debugging techniques.

What is Debugging?

The Wikipedia page for debugging gives us this definition:

Debugging is the process of finding and resolving defects or problems within a computer program that prevent correct operation of computer software or a system.

While the origin of the term “bug” is somewhat debatable and may go as far back as Thomas Edison, the term “debugging” is often attributed to admiral Grace Hopper (sometime in the 1940’s), a computer scientist and rear admiral in the U.S. Navy. The story goes that while Hopper was working on a computer system, a moth was found in one of the electrical components of the system and was preventing it from operating as intended. Upon removing the moth from the component, Hopper said that they were “debugging” the system! Isn’t it amusing to learn the origin of the term was actually quite literal?

Certainly, the act of removing literal bugs doesn’t apply to software and systems today, but the principle does indeed remain the same. When a software developer is debugging a program, they are aiming to remove something that is impeding a program or system from behaving in its intended manner. For me, I find the process of debugging software to be an exercise in applied problem solving, and one I find to be quite enjoyable (most of the time). Sometimes though, a software developer can’t begin solving a problem before they can understand the cause of the problem. Often, working towards understanding the source of a software bug will represent the greatest amount of work or effort involved in the debugging process. To better understand how software developers go about the work of understanding the source of a bug, let’s examine a few common techniques.

Debugging Techniques

Let me begin by saying that this section is in no way intended to be an exhaustive list of techniques and tools that a software developer can use when debugging programs. Now, with that disclaimer out of the way, let’s look at a few debugging techniques.

Reproducing the Problem

Some might argue that reproducing a problem or bug isn’t really a technique, but a requirement in the debugging process. I’ve included “reproducing the problem” in this section because I believe that reproducing a reported problem or bug can be one of the most helpful ways to uncover the root cause of a bug, or at least point a developer in the direction of the root cause of the problem. So, in order to reproduce a problem, a developer needs to be able to recreate the conditions under which a bug/problem is experienced, though this can be much easier said than done. Ideally, when a bug is reported, it will contain some details as to what a user was intending to do, expectations as to what the behavior would be and the actual behavior the user experienced. A developer could then take this information and go about recreating the scenario and reproducing the bug. If the bug can be reproduced, a developer can then move forward in the process and further examine possibilities as to the source of the bug.

Print/Trace Debugging

Print or “Trace” debugging may be the “simplest” technique covered here. Print debugging is really what the name implies, it involves a developer adding print statements to the code they’re debugging. Typically, these print statements would be output to some kind of log file or console that can be monitored by the developer. The actual content of the print statements is completely at the discretion of the developer and will vary based on what exactly is being examined. This technique is commonly used when a developer wants to examine the value of variables in a program or to print out a message when a specific piece of code is executed in the program. For example, consider the following javascript function which should take two numbers, add them, and return the result of that addition:

function addTwoNumbers(x, y) {
    return x + y;
}

Let’s say that, for some reason, this function isn’t returning what the developer expected. A developer could use the console.log() function of the javascript language to print out various information about what’s happening within that function:

function addTwoNumbers(x, y) {
    // print the values of x and y
    console.log("the value of x is " + x );
    console.log("the value of y is " + y );
<span class="c1">// print the "type" of x and y</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"x is a "</span> <span class="o">+</span> <span class="k">typeof</span><span class="p">(</span><span class="nx">x</span><span class="p">));</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"y is a "</span> <span class="o">+</span> <span class="k">typeof</span><span class="p">(</span><span class="nx">y</span><span class="p">));</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>

}

Using these console.log() statements, a developer can check the values of the arguments passed into the function and also check what “type” (number, string, etc.) the arguments are by using the typeof() function.

This is absolutely a contrived example, but it should hopefully illustrate how print debugging works in practice and how it could be useful to a developer. Personally, print debugging is usually the first technique I employ when debugging a program once I have a lead on the location of the buggy code.

Interactive Debugging

Often, developers will use an IDE (integrated development environment) when working with software projects. A common feature of IDEs is to enable the developer to interactively debug their program. For me, I will often use my IDE, Visual Studio, to interactively debug a program through the use of breakpoints that I set in the code. When working in the IDE, I can click on a line of code that I’m examining and then run the program through the IDE’s debugger. When the program executes and comes to the line of code where I have set a breakpoint, the program’s execution will be paused at exactly that point. This enables me to examine the state of the application at that point (what are the values of certain variables at this specific point of execution?), and then I can further step through the execution of the program, line by line, in order to closely examine exactly what the program is executing at specific points. I find this technique to be incredibly useful in the process of identifying the root cause of a bug.

Crash/Memory Dump Debugging

Sometimes bugs will cause a program or application to simply crash, or stop executing. This type of scenario can be very hard to debug, especially when a bug or problem cannot be reproduced by a developer. So, in order to examine the program’s execution and state when it crashed, a developer can utilize a “crash dump” or “memory dump” of a program when it crashes. Producing a crash dump often requires some additional software or configuration of an operating system, so there’s definitely some overhead in the act of acquiring a crash dump. When a crash dump is acquired by a developer, it will contain a vast amount of information of the program and its state at the point where it crashed. On Linux and Mac systems, a crash dump file can be used inside of a program called GDB (GNU Project Debugger), which can provide a developer with the ability to look at exactly what line of a program was being executed prior to it crashing. While this technique can be extremely useful to a developer, there is a lot of overhead that goes into being able to get to this point, so it’s not often the first stop for a developer investigating a bug reported by a user.

I hope that this has been an informative introduction into the world of debugging software. It’s certainly not the most glamorous part of the software development process, but it’s a necessary one! I would posit that being successful at debugging software is a very important skill for software developers. Writing reliable software is rarely ever done, in that a program or application necessarily changes over time: new features are introduced, features are modified, the underlying platform (a browser or operating system, for example) on which the software runs will change, etc. As such, a developer needs to have the ability to competently debug a program to ensure that it will behave as intended.

Protect Your Business Data

We are passionate about helping our customers protect their data. We want you to use Jungle Disk to protect yours. Click on Sign Up to get started. It takes less than 5 minutes!

Sign Up