Various Types of Software Bugs - Vol1
This is a two part article. Read the second part - Identifying Software Bugs - Part 2 - from the inline link.
It’s been said that every line of code is a liability. We have to write code to create software, but every time we do we create the possibility of software bugs—or “bugs” for short. But what is a bug exactly?
A bug is something that is either entirely wrong or not quite right with software. It can lead to wrong outputs or behaviors, or even crash the whole system. The word bug was first used by Grace Hopper, when she taped an actual bug into the documentation she wrote about Mark II. In those days, computers used mechanical relays for calculations. This bug was caught into one of those relays and led to issues with the machine.
If you write code that doesn’t do what you intended it to do, it’s a bug. Bill Gates famously said, “It’s not a bug, it’s a feature.” Meaning that sometimes people have different opinions on whether or not something is a bug.
If a developer believes a list should be sorted alphabetically, but QA expects it to be sorted by date, the alphabetical sorting could be seen as a bug. This is why it’s crucial to have a functional specification on hand. If you’ve written down what is expected and every stakeholder agreed on it, nobody can say they said otherwise later.
This series is about types of bugs and how to classify them. We will start with the basics in this article and get into more obscure bugs in future posts.
Errors, Defects, Bugs, and Failures: What’s the Difference?
Across the internet, you’ll find people referring to anything that could be wrong with software as a bug. But there are different types of bugs, and knowing how to reference them will make issues more tangible when working on a software project.
A mistake in the coding phase of an application is called an error. Developers see errors all the time. It’s common to run a program that will error, but the error isn’t a concern yet because you know the routine hasn’t yet been written. For example, if you write a line of code that uses a function you know you’ll implement later, your IDE will still throw errors to tell you that the function doesn’t exist.
An error found in the testing phase of an application is called a defect. If an error isn’t fixed in the development stage and quality assurance (QA) finds it, that’s when the error becomes a defect.
If a defect is accepted by the development team, it becomes a bug. If the developer is told about the defect, they have to decide if it’s a bug or not. Sometimes it’s clear—for example when the application crashes—but sometimes it isn’t. In these cases, an intended behavior could be seen as a defect by the QA team.
A bug found in production is called a failure. When the QA team misses defining an error as a defect, it can’t tell the development team about it, and it never becomes a bug. If the issue isn’t identified before the software goes into production, it ends up as a failure for users to discover. Therefore testing in production or debugging in production is a very critical mission to be taken serious care of.
Classification of Bugs
Proper classification of bugs can help improve the testing and QA processes. If you have an effective debugging toolset, it will be easier to attack the bugs when you are familiar with the different classes you are likely to encounter.
Classifying by Priority
One way to classify bugs is by priority. In short, how important is it that the bug is fixed? Is debugging in production a must?
Some bugs will render the application unusable, so they should be prioritized as urgent. Others are just cosmetic problems. While it would be nice if they were fixed, their impact on the application is minimal, so they have a lower priority.
The in-between is where it gets hazy. How to prioritize a fix can be highly dependent on the software you’re building and the business goals you have in mind when doing so. Sometimes a performance problem will get a higher priority than a UX problem, depending on the goals of your software.
Classifying by Nature
Another way of classification is by the nature of the bug. How does it affect the application and its users? Can their password be read via an API call, or does a sorting feature not sort correctly? Below you’ll find descriptions for a range of bugs your team may encounter.
An error is of functional nature if the application’s behavior is not compliant with its functional requirements. Functional testing is a good way to find these defects. If a sort button doesn’t sort, but everything else works fine, this would be a functional bug.
Runtime errors are operations that a program attempts to make but are impossible to carry out. The simplest example is dividing by zero. This can lead a whole application to crash, but it can also simply cause a wrong calculation result that gets carried around the application without ever generating a crash.
Issues with resource consumption, latency, and stability are classified as performance problems. Again, these depend on the requirements. It could be perfectly fine for some software to take ten minutes to carry out a task, but even ten seconds would be too long for others.
If a user doesn’t understand how to use your software, or it’s too hard to complete important tasks, these are usability issues. If no functional specification was given, or it wasn’t thorough enough, it can be a matter of opinion as to whether or not these are bugs.
Compatibility errors can arise when an application has to run under different browsers, operating systems, or hardware. They reveal themselves in the form of inconsistent behavior or performance on these platforms.
Possibly the most important part of your application is security. There’s never a good time to open your system to malicious people trying to steal your data. Such a breach could cost you the trust of your customers and even make you liable to them.
The user experience shouldn’t be overlooked when building an application, and communicating how to use your software is part of that. Plan your features with real use cases and document their addition, otherwise, people won’t be able to find them.
Classifying by Testing Types
You can also classify bugs by the testing method that will find them. Below you’ll find a list of these tests—including syntax checkers, unit-tests, integration-tests, and more.
Syntax errors are found by an IDE’s syntax checker while typing code, or later when the compiler or runtime of the programming language has to actually execute the code. Missing characters or typos are usually the cause of this kind of error.
Calculation errors are found by a type-checker if your programming language offers static typing. Calculation errors may occur due to bad logic, an incorrect formula, a data type mismatch, coding errors, or function.
Bugs discovered during unit testing are mostly related to business logic.
Often an application is implemented by different developers and their modules have to work with each other. Integration tests can find system-level integration errors. These usually crop up when you try to integrate the code you wrote with other systems.
Out of Bounds
This happens when a user enters a string where it's supposed to be an integer. It can also mean that you tried to access an array element with a bigger index than the array’s size. These kinds of errors can be found with fuzzing tools.
The control flow of software describes what it will do next and in what condition. Control flow problems arise in development when software accumulates new code over time. This is especially true when code is added ad-hoc, rather than being correctly refactored. A cyclomatic complexity scanner can help to keep these kinds of problems in check.
An operating system usually comes with a number of command-line interpreters. These can handle different types of scripting languages. If your application uses shell scripts and your user’s computer doesn’t have a shell interpreter installed, it won’t work. The same goes for all interpreted languages.
Errors about errors? Yes. Sometimes you can’t fix a problem without user compliance, so it is essential to display meaningful error messages. Error handling is especially important for background processes, like batch processing, or talking to a remote server. When they fail, a user needs a clear message on why they failed so they can either solve the problem by themselves or give it to technical support.
To Be Continued
There are many types of bugs, and a sufficiently large software project will get all of them in its lifetime. Properly classifying these bugs is an important tool for getting rid of them. Classification enables you to sort a mound of problems into manageable bundles that can be categorized and prioritized for efficient and logical resolution.
It’s also important to remember that if you don’t specify the application’s behavior upfront, some bugs can become a matter of opinion. Write down what is needed, so you don’t get lost in endless debate.
In the next post of this series, we’ll look at more exotic bugs, shedding light on the dark world of Heisenbugs, Bohrbugs, Mandelbugs, and Schroedinbugs.
A Little Hint
There is a tool to catch and stamp out various kinds of bugs called Sidekick. Sidekick is a live debugging tool that supports application debugging in remote environments with its IntelliJ IDEA plugin and VSCode extension. These environments might be either production or pre-production environments. Sidekick provides non-breaking breakpoints, called tracepoints. It integrates remote debugging with distributed tracing and provides end-to-end observability into cloud applications.
If you have yet to take your first step into the world of Sidekick, you can begin your journey here.