Underneath the UI, the experience, and the features, a computer program is a set of instructions that the system has to interpret. Imagine that a computer is like a human that you have to spell things out as explicitly as possible, without making a single spelling or punctuation error.
Unlike human cognition, machines can’t use heuristics (usually) to guess at what a miswritten instruction means. Thus, a missing semicolon can break thousands of lines of code. Sometimes the code won’t run at all, at other times it will run, but not work as it was originally intended. These defects or errors are commonly defined as bugs.
Testing the software doesn’t reveal the cause of a bug, it just shows us the effect. Some error outputs try to make an educated guess to help the developer pin down the source of the bug, like Python’s interpreter. Others can be extremely frustrating and obtuse.
No software developer wants to write buggy code, but it’s an inevitable part of the process. As such, debugging is an inherent part of software development. Fortunately, debugging can actually be a great asset in the development process, leading to novel solutions and alternatives that will make the software better in the long run.
How long does debugging take?
It’s always surprising to non-software developers when they hear that debugging actually takes more time than writing the code itself. When building software, most of the time is spent either debugging or maintaining the code rather than actually writing it.
Obviously, the implication is that we should try to avoid bugs in the first place. One of the most common practices is what it’s traditionally known as “defensive programming.” Explaining in detail would require an article in itself, but for now, it’s enough to know that it’s writing code while having the worst-case scenario in mind.
Think of it like this: you are driving a car down a highway and you make decisions as if every other driver on the road is about to make a terrible mistake. In that analogy, the other drivers are the users and you are the software developer. For example, a function that may otherwise be perfect can break when the wrong argument is passed. What if the developer preventively builds it in such a way that it accounts for that kind of scenario?.
What causes bugs?
Generally speaking, bugs can be classified according to the kind of error:
- Logical or semantic errors: With these kinds of errors the code’s syntax is just fine, the problem is with the underlying logic. Something about the algorithm isn’t working as originally envisioned which leads to unexpected outputs. It’s like following a recipe to make pizza, opening the oven, realizing that you cooked chicken. When you check the recipe you realize that it was a chicken recipe all along.
- Implementation errors: The high-level code is working as expected, but some of the low-level data structures are incorrectly manipulated, leading to either bad outputs, or the code not running. For instance, a Python function may try to move the order of items in a tuple (a tuple is a data structure that is ordered and unchangeable). That’s not going to work.
- Typos and simple errors: Missing a parenthesis, using the wrong logical operator, calling the wrong variable – it’s all the equivalent of misspelling a word or using commas when you are not supposed to.
- Syntax errors: Something is wrong with the code and the computer can’t understand the instructions. For example, some programming languages use a semicolon to denote when a line ends. Missing a semicolon means that the computer will keep on reading thinking that other lines are part of the same instruction.
Bugs are symptoms of an underlying problem. In some cases, the bug will point straight to the issue, other times (more often than not), it’s up to the developer to comb through the code to find the source. If you are dealing with a hundred lines of code, that’s doable, but software can have millions of lines spread throughout multiple files. That’s where debugging strategies come into play.
What are some of the most common debugging strategies?
- Incremental development: The developer tests the code as they build it, every few lines they run a test to see if the code is behaving as they expect. On the downside, this increases the time it takes to write code.
- Visualization: The sister method to incremental development, software developers print outputs or use console logs to see how the code is behaving. This is often done in small chunks to analyze how the data is being manipulated in each step of the process.
- Use debuggers: Most popular programming have debuggers, that is, a piece of software that analyzes another software and finds errors. They usually provide a lot of information to help the developer pinpoint the problem.
- Problem simplification: The developer simplifies the code and starts experimenting with small chunks, adding more complexity to it until the error is found.
- Bug clustering: Similar bugs may point to the same underlying problem. By categorizing errors, the developer can have a better idea of where the issue lies. This is like a doctor using several symptoms to diagnose a disease.
- Backtracking: It’s rather simple, the developer begins with the error and goes back through the code to find where the problem started.
That’s just a small sample of literally hundreds of methods and styles used to debug code. As you can see, many of these strategies (almost all of them, actually) have the developers going through the code again and again, and therein lies the potential of debugging.
As the developer team reviews the code and searches for the source of a bug they will find themselves looking at their previous work in a new light, and like any change of perspective, it can lead to improvements in both the structure of the code and the efficiency of the software. It’s like fine-tuning an engine!
What role does the client play?
Believe it or not, the client is as important as the developer in debugging, especially in incremental development or when working with agile teams, clients by all accounts are testers, in fact, they are the best testers as they are the ones aware of what they really want.
So, when a client catches a bug firsthand their input will help the development team find it and squash it. As such, the client should always report the bug with as many details as possible, and if the team is using a data-gathering tool, the client should accept and send the report. The more information that is shared with the team, the higher the chances that debugging will be faster and more efficient.
Software development is a system of relations between the team, the client, and the technology itself. A project’s success is due in great part to how this system communicates. If we stop seeing debugging as a patchwork due to a lousy job and we start seeing it as another form of communication within the system, we are building a network of improvement that will end up creating the best product.