Day 29: Fix the Cause, Not the Symptom
A lot of programmers’ time is spent in fixing bugs. As code evolves, it is inevitable that inconsistencies will appear and need to be removed.
Moreover, it has been observed by many in the software development industry that maintenance and bug fixing is the most expensive part of software projects. Therefore, it is not a surprise that fixing bugs is an important part of successful programming.
Clearly, a good part of reducing the cost of bugs is not allowing their existence in the first place. If we can prevent the appearance of errors by proper development techniques, we will be avoiding a lot of work that, as a result, won’t need to be done or repeated later.
A lot of the techniques we discussed in previous topics, such as unit testing, appropriate software design, and coding standards are targeted at this. However, despite the best efforts there are still situations in which fixing a bug is necessary.
Tips to Fix Programming Errors
When bugs are detected, a lot of energy can be spent in activities that don’t lead to an immediate resolution of the problem. If it is not possible to avoid the occurrence of bugs in the first place, we should at least make sure that the bug eradication phase is properly handled.
Here are a few tips that can be used to fix software defects:
Avoid using inefficient debugging techniques: There are cases when a printf is just what you need to detect what is going on in your software. However, there are bugs that are difficult to trace, and printing data at random spots may not do anything to help identifying the issue.
Good judgement is necessary to identify such cases. Do you think that it is better to find the relationship between several variables at a particular moment? In that case, it is probably better to use a debugger with a breakpoint set to a particular line. On the other hand, if it is important to observe the a sequence of values produced by the program over time, then it is probably wiser to have a printf of a particular variable. Again, it is a case of using the proper tool for each job.
Devise a debugging plan: The biggest problem when debugging code is to start making changes without understanding what is really going on. To identify the exact place where the problem is happening, you need to come up with a plan. The investigation that follows can be used to find out exactly where and why the error was caused.
Find a way to reproduce the error: In debugging, a lot of time is spent on tracking an error. However, the most important thing is to determine exactly how to reproduce the issue. If there is no certainty about how the bug can be reproduced, there will be no way to guarantee that any particular change in the code has fixed it.
Before doing anything else, try to determine the exact condition that triggered the bug in the first place. Also, make sure you have a proper way to achieve the same results in a controlled way.
Fixing the root cause and not the symptoms: A hallmark of unexperienced developers is the belief that you can fix a bug by removing the symptoms. This is a dangerous way of thinking, because it can make a difficult situation even worse.
When there is a bug, it is good that it become visible, so that developers have a chance to find its cause and close it. On the other hand, by fixing the symptoms of a bug, the root cause will continue undetected. Eventually, it can cause an even worse problem, and when this happens it will be even more difficult to detect than in the first time — because now the symptoms cannot be easily observed.
Create regression tests: a regression test is a tool used to reproduce the exact conditions that generated a bug and guarantee that it will not be present in the future. The reasoning for applying regression tests is that if an error happened in the past and fixed, we should make sure that will not reappear in the future.
A good method to implement regression tests is to create unit tests that exercise the intended behavior. The tests will make sure that the software will always provide the expected answer in the future.
As developers, we should do the best to avoid bugs in the first place. However, it is inevitable that errors will appear in our code over time. When this happens, we should strive to deal with errors responsibly and with a proper plan. Moreover, avoiding to deal with bugs is self defeating, because it will most probably cause other errors down the line, which will be even harder to fix.
About the Author
Carlos Oliveira holds a PhD in Systems Engineering and Optimization from University of Florida. He works as a software engineer, with more than 10 years of experience in developing high performance, commercial and scientific applications in C++, Java, and Objective-C. His most Recent Book is Practical C++ Financial Programming.