The Art of Debugging


I recently wasted a lot of time while attempting to use the amazing Dropzonejs image uploader. I left out a function call in an overloaded function and this broke every subsequent call. More annoyingly, I could not pinpoint the cause of the error as Dropzone was failing silently.

Close to giving up, I decided to give it one more attempt and went to my sandbox – I create a safe area in all my projects for doing ‘risky’ stuff. Starting afresh there, I was amazed to see that everything worked fine. A close comparison of the code samples revealed the bug’s origin – the left out function call. I double-checked to confirm this and that was all about it. Sadly, the documentation does not mention that a failure to call done() in the accept function will break the code silently.

Programmers usually spend a lot of time debugging and it can be a painful experience;  some feel like tearing out their hair in exasperation, smashing their poor computers or even believing that their computers really ‘hate’ them! :)

Actually computers do not hate people and the operating system is not conjuring bugs – the most likely reason is buggy code. Here are a couple of tips on debugging; hopefully these will help to reduce time spent, frustration and annoyance levels.

The Inefficient Way of Debugging: Trial-by-Error

Random guesses? Print statements littered around? Fast fixes that ‘appear’ to work? Do these ring a bell? Welcome to the bad bad way of debugging.

This approach is fraught with a gazillion issues. The worst being the potential introduction of new bugs into the codebase. Lucky bug discoveries offer little learning opportunities and it might be difficult to reuse knowledge gained from such fixes in the future.  Also, such run-of-the-mill fixes tend to be inelegant and stand out like a sore thumb when compared to earlier designs and architecture.

The Efficient Ways of Debugging

1. The very first thing is to create a list of the likely causes of the bug; this usually involves thinking deeply about loopholes and design flaws. Studying program error dumps and buggy behaviour might also help.

2. Go through the list while eliminating disproved hypotheses. Once a likely cause is found, a verification needs to be carried out; this can be as simple as proving that the bug appears only when that cause is activated.

3. Don’t just rush to write a fix – I bet you would not want your ‘super-fix’ to mutate into a monster ‘bug’. Think deeply about the bug and what potential ripple effects a fix could have. Also think about how the proposed fix would work, how it blends into the system’s architecture and possible design impacts.

4. Yes, you got it!! Go ahead and squash that bug! Yuck – I dislike bugs.

5. Not finished yet, you need to go through your code armed with your newly acquired bug-terminating powers. Find and ruthlessly sanitize code likely to suffer from such bugs. Rinse and repeat – always leave code in a cleaner state than before.

Extreme Approaches: When the Efficient Ways Fail

You might apply all the above and still be stuck in a rut; this might occur if you have extra-terrestrial bugs (quite popular in multi-threaded environments).

6. Total examination

The brute force approach is bound to work insha Allaah although it involves painstaking work. You go through the code thoroughly (a debugger should come in handy); this might involve examining all execution paths if possible and trying to pinpoint the issue. Hard work…

7. Start afresh

When everything fails, why not throw out the old code and write it again?

Some other tips

6. Very important, set a time limit

Do not spend 3 days hours trying to fix a bug if you can re-implement the same feature in 2 hours. If you are sure you can rewrite a bug-free version of some buggy code, go ahead with a rewrite and spare yourself the trouble. Time counts.

7 Learn to use tools

Debuggers, memory examination tools and profilers. They will help point out to what might be causing the issue. I once had a really really nasty bug while using jQueryUI, the bug only showed up on a particular browser and only when the page was accessed over the local network; otherwise all was fine.

I eventually had to use a profiler after several failed debugging attempts, I  then discovered that a function was being called several hundred times. Bug case solved!

8. Discuss with a buddy

Find a programmer / buddy to discuss with, discussing your ideas with someone will help you to find design gaps and flawed assumptions; you might be surprised that you get the error spot on without any contribution from your buddy.

Now go ahead and eliminate some bugs :).

9 Comments

·

Leave a Reply

  1. I appreciate the way you offer analytic rigor to the debugging process. Often, stepping back, taking a deep breath, and approaching the problem without that sense of urgency and fear leads to much more productive and thoughtful debugging sessions. I also love rubber ducking.

    Even still, I’m surprised that you didn’t describe the single most important aspect of tackling bugs in software: TDD. This involves a leading and trailing activity. The leading activity is architecting the solution with the specification first (the unit tests are fundamentally specs). You most likely did not unit test that js function described above and ran into some trouble down the road. I absolutely love jasmine bdd (http://pivotal.github.io/jasmine/) for javascript testing. The trailing indicator is when those inevitable bugs pop up, the developer applies the rigor you outline above, and discovers the bug, the unit test should immediately be written to test that edge case.

    Without TDD, as you describe in step 3, we are quite prone to seeing that bug again. Thanks for helping us all remember some of the fundamentals of software development – thinking! :)

    Like

    • Thanks a lot Chris.

      Yes you are right however I do not use TDD; I only tried it out once and didn’t really fancy it.

      The issue itself was the documentation of the library – it didn’t mention that a call in a particular function was compulsory. I doubt if TDD would have helped much in this scenario too.

      I appreciate your feedback and excellent contribution – thanks a lot.

      Like

      • I kinda agree with you. TDD is a great tool in a programmer toolbox however, many times in practice, using TDD or even automated testing isn’t the best thing to do.

        For example, for relatively small projects it is better to spend your time focusing on architecture, code quality and tools.

        And this is mostly the case for many open source projects, since they have limited manpower it is often more appropriate to cut down on some TDD and automated testing.

        Fortunately, writing property-based tests (e.g: Quickcheck), in many cases, takes less time to write and deliver more value (if used correctly).

        Like

  2. I feel you bro. In any coding environment debugging can quickly become a nightmare. Thanks for taking the time to share after all the time it took for you to find your bug(s).
    Good one.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.