Debugging code is very much like detective work. Analyze clues, look for evidence of what’s happened, form a hypothesis. In fact, since debugging shares strategies with professional sleuthing, it’s even possible to leverage wisdom from that field. For instance, one of my favorite detective quotes comes from author Sir Arthur Conan Doyle:
“Once you eliminate the impossible, whatever remains, no matter how improbable, must be the truth”
Following this adage goes a long way to help with debugging code. Of course, we as developers probably have some idea of a bug’s cause before we start investigating it. Sometimes it’s easy to find the “smoking gun,” verify the problem, and make the fix. Other times it’s not so straightforward.
Get something to work
In those situations where it’s not clear what the culprit is and I feel like I’m spinning my wheels, I always try to get something to work—no matter how basic—and then build on that success in baby steps. For example, I was debugging an iPad app issue where you’d tap a calendar entry and a screen would come up with details about that event. Trouble was, the app would crash a second after the detail screen came up. To make matters worse, the bug was only showing up on iPads that had been recently upgraded to the current version of iOS.
After trying a few things (with no success), I applied my debugging technique of just getting something to work, no matter how basic. I was worried there might be some iOS issue that was giving the detail controller issues, pointing to a deeper issue that could be harder to fix. But this was just a guess. Also, since the client had a policy of using the latest iOS version, we didn’t want to tell them to revert back to the iOS version it was working in. We had to come up with a proper fix for the problem.
My first step was to comment-out all the logic in the detail controller, except for the bare essentials of opening up the window in the navigation controller. Success! I was able to tap on the calendar entry and bring up a detail screen without a crash. Of course, the detail window was completely blank, since I commented-out everything. But no more crashes, even using the current version of iOS.
Let the code lead you
The task now was to figure out the root cause of the issue. Since I didn’t have a clear idea what might be causing the crash, I let the code guide me to find the issue. I slowly started un-commenting lines of code in very small chunks, maybe a few lines, maybe a single line and re-testing. More baby steps of success.
After a few iterations of uncommenting and retesting, I was able to isolate the section of code causing the crash. In true Holmes fashion, I “removed the impossible” by slowly reintroducing lines of code to see if it was the culprit. After each iteration, I knew it was impossible those lines were causing the crash since the app was working with those lines running. Whatever remained (the code that was still commented out) were still suspects in the crash.
After a bit more removing the impossible causes of the crash and seeing what remained, I was literally shocked. What was causing the crash? Setting the border on a view on the screen! I couldn’t have been more surprised, but that was the single “remaining” line of code and was indeed the cause of the crash, and the truth I’d been searching for. And it was supremely improbable!
Now that I knew what was causing the issue, I still had to devise a solution that would work with the current version of iOS. After some research, I found that updating to a more current framework version solved the problem and stabilized the app.
Fix the real problem
Debugging troublesome code can be very frustrating for developers. After spinning your wheels and not getting anywhere, it’s easy to start guessing at what might be a reasonable explanation for a particular bug. Problem is, then you start trying to fix what you think it might be, without having a smoking gun to prove it. This can lead you to add code to fix a non-existent problem which could then cause even more bugs. To properly fix a bug, you need a clear and unobstructed view of the true problem. This means letting the facts and code lead you to the answer, not jumping to conclusions.
Bugs are a natural part of software development. We always try to keep software bugs to a minimum, with good design and forward-thinking architecture. But bugs will always show up. By following best practices to detect the true bug, fix it properly and perform appropriate unit testing, most bugs can be squashed, to never return.
If you have bugs in a software project that just won’t go away, or you want to start a new project and make sure the bugs are minimized, please don’t hesitate to reach out to Anexinet. We’d love to help you get started.