Any software builds upon other software – nothing truly starts from scratch. Even the most trivial “Hello World” demo program relies on a compiler, (most likely) a standard library, and then all of the low level system services, such as the operating system, drivers, and hardware. In any of those areas, it’s pretty much certain that there are bugs, and in all of those areas, there are guaranteed to be features that would be nice to have. So what does one do when a bug is discovered or a need for a new feature arises?
The Two Schools of Thought
There are two schools of thought when it comes to these bugs and feature requests in dependent software (and hardware):
- Work around the problem / implement the enhancement and move on (keeping the work to yourself)
- Report the bug or enhancement request to the dependent software’s maintainer/vendor (also known as “upstream”), then wait for them to do what is necessary, working with them as necessary
The “Easy” Way
Option 1 is the so called easy way – for at first, it seems easy. The basic idea is that if you encounter a problem, work around it. Let’s say a developer is graphing some data, and finds that if certain types of data are graphed, the graph doesn’t look right (perhaps infinities aren’t handled in a way that makes sense). The obvious approach is, whenever an infinity is encountered, substitute a value which makes the graph look acceptable (perhaps a really big but not infinite number). In a matter of an hour or two, the bug is solved.
This approach seems great – the problem was solved quickly. However, perhaps there are multiple graphs in the application – this workaround must now be made to all the areas of code that perform graphing. And when a new graph is introduced, we have to remember to add this workaround.
Worse yet, now consider that some time has passed and the project is done and the developer has moved on. A new developer is now working on the project, and adds a new graph – and is baffled by why infinities don’t work right. Time is lost as the new developer has to re-discovered the original developer’s workaround.
And even worse, let’s say the original developer is now working on a completely new project that happens to need graphs. Having experience with this graphing library, he chooses it for the new project. And once again, he gets a bug report saying that infinities are not handled properly. And once again, he must re-discover and re-apply the workaround all over this new project.
The end result is that there is substantial time wasted at multiple levels with lots of unhappy people:
- The developer loses time by applying the same fix in multiple places
- The testers/QA lose time by continuously finding the same bug in multiple places
- The project managers get annoyed as the schedule is less than predictable
- The client is unhappy because a bug that was “fixed” keeps reappearing
- On subsequent projects, all the same time loss and malaise occurs again and again
The Right Way
Option 2 initially seems like more work but ends up being a significantly lower effort for everyone. Continuing the previous scenario, if instead of simply making a one off fix, the developer immediately reported the bug to the charting library’s project. He spends a few minutes to come up with a test case, fills in the form on the charting library’s bug tracker, and then moves on to something else. There are two things that happen at this point: either someone will reply to the bug with a fix, or not.
If someone replies with a fix, and that fix gets included in the charting library, then great! The developer never even needs to figure out the bug – he simply applies the fix to his copy of the charting library or upgrades the charting library. He immediately saved time and effort, and the problem is fixed for this particular chart, all other charts in the project, and even all other projects.
If no one replies, the developer eventually goes back and tries to fix it himself. He figures out the problem, and now posts a patch to the bug tracker with the fix. With luck, someone accepts the patch into the project, and as with the previous case, the bug is fixed for everyone. In this scenario, the developer may have actually spent more time on the bug than in the “Easy” Way scenario initially (as he had to collaborate with the charting library project, get the patch to be acceptable, etc) – but this bug won’t be coming back. And, because the experts from the charting library project reviewed the work, we can be sure the work is good, maintainable, and doesn’t introduce other problems.
In this scenario, the project manager is happy – the same bug isn’t being reported over and over. The developer is happy – developers hate fixing the same thing multiple times. And the client never encounters that annoying scenario of having to question why the managers/developers are not fixing things they claimed to have fixed. Plus, other managers and developers out there are unknowingly happy, as they will never encounter this problem.
In the Real World
In the real world, projects tend to not think ahead. As deadlines loom, projects enter “fire fighting” mode – rather than take 5 minutes to do something right, the response becomes “just hack it and worry about it another day.” The same goes for documentation, testing, and good architecture. All of these hacks add up to horrible technical debt, leading to a scenario where a project is in constant fire fighting mode and can never catch up, constantly fixing bugs caused by earlier hacks, laying hacks on hacks. This spiral results in turnover of the managers and developers and very unhappy clients.
Let’s take an example from a project I once worked on. The project was an Android app that, among lots of other things, graphs some data. Rather than reinvent the wheel by creating my own graphing library, which would be to say the least a large project in of itself, I chose to use achartengine. The library had a small community and was pretty new, so it had a decent number of bugs, and could use some features.
The project deadline (as always) was very tight. But, I was determined to do things “The Right Way” and have the team do the same. The result, for example, was this bug about how infinities are graphed incorrectly. There were also other issues, such as this one about zooming not working right, and bar charts not handling nulls properly. I reported bugs in worked with other projects as well, such as Spring and Android. Not everything is fixed – but even when it’s not (such as this example from Android), the team learned something, and ultimately the project became better and more efficient. The code is clean, high quality, and easy to work with. In dollars and sense terms, it has a lower TCO (better maintainability) than if we had “done things quickly.”And, based on previous experience, if we had “done things quickly” (aka the “easy” way) the project would have actual taken longer.
When the Right Way Isn’t Possible
Doing it “The Right Way” is great when it’s possible. However, there are cases where it is actually impossible.
- The problem is in hardware, and replacing it isn’t feasible. In that case, reporting the issue is still important as the hardware’s creator could help design the best workaround, but the end result will be a workaround and not a new, fixed version.
- The bug is in proprietary software that doesn’t accept bug reports (Microsoft for example… they often times charge fees to report bug in their products, then take years to provide a fix if they ever do at all)
- The problem is in software that cannot be replaced/upgraded/modified for any number of reasons (legal, compliance, etc). Again reporting the is issue is still important so the author could potentially help in formulating a workaround.
In that case, well… good luck 🙂 The best that can be done in such cases is to have plenty of comments in code and documentation (especially in the relevant bugs in the project’s bug tracker).
The Importance of Upstreaming Issues by Craig Andrews is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.