The best software developers don’t just write code — they also debug it. Whether you wrote your current code base or inherited it, this primer provides a framework for best practices.

Yellow Rubber Duck Sitting on Laptop As Focus Aid For Software Engineer during Code Debugging
Image: rohawk/Adobe Stock

The zero bug software myth

All software has bugs. This is a fact that anyone who has been in the software development business knows. It would be incredibly cost prohibitive to try and root out every minor defect in an application of any complexity. Never mind that software testing ultimately only proves the existence of issues, not their absence. That said, there are certain types of defects or software bugs that need to get corrected.

SEE: Hiring kit: Back-end Developer (TechRepublic Premium)

The usual suspects

There are a number of factors that contribute to software errors. Below are a few of the most common:

  • Ambiguity in expected behavior
  • External subsystems or components
  • Insufficient testing and timelines
  • Programming errors

Regardless of the culprit, at some point in your career either you will decide, or someone will decide for you, that a defect must be addressed. How this determination is made varies, but it is usually some combination of customer behavior, actions, loss tolerance and minimization. Most competent software developers can fix an issue once the cause has been uncovered. The process of uncovering the cause is referred to as troubleshooting or debugging.

Troubleshooting vs. debugging

In simplest terms, debugging is a subset of troubleshooting. You can troubleshoot without debugging, but you can’t debug without troubleshooting. If that sounds confusing, it’s because it is.

Troubleshooting tends to happen at a higher level of abstraction than debugging and applies to many components of the system. It’s the process of parsing out the component or components of a system that are causing problems. Troubleshooting can be applied to any system, and does not imply that the issue will be fixed, rather the focus is on finding and understanding the root cause.

Debugging on the other hand is specific to computer code. As a developer, if you are tasked with debugging some software module, you are being asked to find the instructions causing the unwanted behavior and correct it, all as part of the same session when possible.

Documenting bugs

The first step in troubleshooting and debugging undesired behavior in software is making sure the issue is properly documented. This will ensure all parties involved have the same understanding and it helps to inform later steps in the process. At a minimum, a properly documented bug covers the following:

  • Description
  • Steps to reproduce
  • Reproduction rate
  • Expected results
  • Actual results
  • Environment and version

Estimating bug fixes

As a developer, you will often get asked to estimate how long it will take you to find and fix a bug. Unfortunately there is no good answer. A recommended practice is to timebox your initial efforts to 4 hours or half a day. During this initial session you should be concentrating on uncovering a few key nuggets of information.

  • Can you reproduce the issue?
  • Do you know the root cause?
  • Do you have it narrowed down to an area or component?

If you are able to find and fix the issue within the initial timebox, congratulations. If not, it’s probably appropriate to go ahead and provide some sort of t-shirt size estimate based on your initial debugging effort to stakeholders. Below is some general guidance, but be warned your mileage might vary:

  • Small: Requires 4-8 hours to solve.
  • Medium: Requires 2-3 days to solve.
  • Large: May require a week or more to solve.
  • X-Large: Will require additional team members and/or planning to solve.

At this point you may be asking why provide any sort of estimation at all if estimating bug fixes is such an inexact science. The truth is it’s all about providing transparency both to help manage stakeholder expectations and provide product owners with options when or if it becomes apparent that doing nothing until the fix is available is not an option. Albert Einstein is credited with once saying: “We can not solve our problems with the same level of thinking that created them.”

Tried and true debugging techniques

Every modern IDE has a plethora of debug options. These are usually specific to your programming environment and are meant to make your life as a developer easier. That said, don’t overlook the basics.

Print things: A lot of things

Logging is still one of the most effective ways to troubleshoot an issue. You’d be amazed how many times logging reveals the code you’ve been troubleshooting isn’t even getting called.

Read the error message

Googling an error is a great way to see how those who came before you may have solved something, but make sure you take the time to really read the error message first. Most modern languages and frameworks will give you a good idea of what the error is, and many suggest fixes that are specific to your situation at the very moment the error was encountered.

Start with code that already works

Developers use source control for a reason. Any time you can rollback to a working version and then diff the code bases you’ll be able to narrow down the offending section of code much quicker. Even if the error was only recently discovered, frequently you can step back several versions and then apply updates one at a time until the undesired behavior returns to gain some insight into the issue.

Run your code every time you make a change

It sounds painful, but seriously, running the code and verifying behavior changes as frequently as possible can save you from having to backtrack in your troubleshooting steps.

Divide and conquer

Often overlooked, simply commenting out code until the issue disappears can be a quick and dirty way to ferret out the offending instructions.

Rubber duck debugging

At times troubleshooting software becomes a team sport and requires outside the box thinking. One such technique is known as “rubber duck debugging.” It’s based on the premise that sometimes when troubleshooting software, we need to slow down to go faster. The steps for rubber duck debugging are outlined below.

SEE: Hiring kit: Python developer (TechRepublic Premium)

  1. Beg, borrow, steal, buy, fabricate or otherwise obtain a rubber duck (bathtub variety).
  2. Place said rubber duck on your desk and inform it you are just going to go over some code with it — if that’s all right.
  3. Tell the duck what your code is supposed to do, and then go into detail and explain your code line by line.
  4. At some point you will be explaining to the duck what your code is doing, and realize it is not in fact what your code is doing. The duck will stare at you smugly.
  5. Thank the duck for its time and fix the code!

Don’t panic. Like most things we do in life, troubleshooting software gets easier with experience and practice. The key is to be transparent with stakeholders throughout the process, keep your approach organized and methodical, and don’t be afraid to ask your fellow programmers for help.