Skip to main content

5 Ways to Write Bug-Free Code

  • Author:
  • Updated date:

Can We Write 100% Bug-Free Code?

In the summer of 2015, a team of hackers attempted to take control of an unmanned, highly classified military helicopter known as “Little Bird."

The hackers had a head start, and within a short time, they had already hacked their way into one part of the drone's computer system. From there, they needed to hack into Little Bird's onboard flight-control computer, and the drone was theirs. But they failed, and the reason was a new kind of security mechanism implemented by DARPA (Defence Advanced Research Projects Agency) called the “formal or mathematical verification.”

In this mechanism, critical parts of Little Bird's computer system were made unhackable with existing technology, and its code was made 100% foolproof, just like mathematical proof.

And unlike most computer code which is tested after getting written, formally verified software reads like a mathematical proof. Each statement follows logically from the preceding one. An entire program can be tested with the same certainty that mathematicians prove theorems.

As Kathleen Fisher, the founding program manager of the High-Assurance Cyber Military Systems (HACMS) project, rightly said later.

"They were unable to break out and disrupt the operation in any way. That result made all of DARPA stand up and say, oh my goodness, we can actually use this technology in systems we care about."

The only way to have a 100% bug-free code is to prove the code mathematically. Very few programs in the world are mathematically proven simply because it is way too expensive to be used. Most of us are working on projects that cannot justify the cost of mathematical proof, and that is why we need to rely on our local bag of tricks to maintain the bug rate as low as possible.

That said, we can still write bug-free code, and what I mean by bug-free code is writing software with acceptable quality, developed within the given cost and time. We aim to minimize the bugs by making cheaper mistakes to avoid more expensive ones. This way, we can attain a reasonable perfection level that justifies the project's investment.

Here are five ways to do so.

  1. Don't ignore warnings.
  2. Do Test automation.
  3. Manage program inputs.
  4. Reduce conditional logic.
  5. Listen to the user.

1. Don’t Ignore Warnings

When I was new to programming, I used to build applications, and in most cases, warnings used to spew out in droves. And when I used to ask fellow programmers about it, they told me to ignore them as warnings are harmless.

Really? Are they really harmless? I learned it the hard way and realized that eliminating the warnings always led to fewer bugs and less brittle code. Warnings are simply errors waiting to occur, so ignoring them is dangerous for the following reasons.

  • Warnings are the default behavior of your compiler tool, which might not suit your coding scenario.
  • As warnings accumulate, bugs become harder to fix and prevent, as the code may soon get out of control.
  • Ample warnings mean a lack of team discipline. It shows that the coding standards are sloppy.
  • The team may get into the habit of suppressing all warnings, and this may also result in harmful warnings getting suppressed.

Although it takes more time to fix a warning than to suppress it, it is time well spent and will result in a cleaner, better code in the future. To expose potential bugs, set the warning level of the compiler tool to maximum strictness. Do not tolerate warnings. Eliminate them.

2. Do Test Automation

Test automation increases the overall software efficiency and ensures robust software quality. There are specific tools that can effectively execute automated test cases and help in comparing actual and expected results.

Some of the benefits of test automation can be.

Faster Feedback: It improves communication among coders, designers, and product owners and allows potential glitches to be immediately rectified.

Accelerated Results: Testing can be carried out repeatedly, delivering faster results each time with lesser effort and time.

Testing Efficiency Improvement: Automated tests eventually take up significantly less time. They can be run virtually unattended, leaving the results to be monitored towards the end of the process.

Higher Overall Test Coverage: Automated testing results in more tests being executed on an application. This leads to higher coverage than in a manual testing approach.

Earlier Detection of Defects: Defects getting detected earlier helps increase the overall development speed while ensuring correct functionality across areas. The earlier a defect is identified, the more cost-effective it is to fix the glitch.

Remember having a solid battery of automated tests is an excellent way to decrease the bug rate and avoid new bugs while refactoring code. The initial investment might be high, but the cost justifies the effort saved later in the project.

3. Manage Program Inputs

We all know the age-old adage “garbage in, garbage out." So, if your inputs are trash, you are bound to get bugs.

And all programs need to get data from external sources, which might be unreliable or questionable. The source can be a user interface, file, external system, or database. For example, you expect a credit card number as input, and somebody enters SSN no. You expect US postal code to be entered, and somebody enters the postal code in Indian format. The possibilities of error are endless.

To make your program robust, you must validate every input to your program. I know validation increases program size and reduces performance. Hence, it is crucial to carefully trade the "minimum" that can be accepted and what is absolutely "non-negotiable.”

And while validating, you must ensure that you validate all the data at only one place where your program obtains the inputs. By putting all the validations in one place, you can check for invalid data in an organized way, and there will be no need to validate data at other places.

Remember, managing program inputs is an obvious but often overlooked technique for managing bugs. But it is crucial and cannot be underestimated.

4. Reduce Conditional Logic

I recently saw a ted talk by Miško Hevery in which he proposed an interesting challenge to his audience. He wanted them to write some code with no if-else or switch blocks at all.

Is it even possible? The conditional logic building is the heart of any programming logic, and what can possibly be gained by such an exercise?

Hevery later explains that testing each combination becomes impossible as the number of conditional statements increases and bugs starts seeping in from inconceivable scenarios. Also, too much conditional logic makes the program harder to understand and rectify.

Hevery's point is that code without if (or switch) statements is

  • Easier to read and understand
  • Easier to test
  • Easier to maintain, extend, etc.

While he agrees that the complete elimination of conditional logic is unavoidable, we can reduce the amount using the following ways,

  • Good OO design can eliminate the need to code branching explicitly.
  • Using assertions for validity checks.
  • Using context and polymorphism.
  • Eliminating train wreck code - A train wreck is multiple levels of method calls (called a chain), in which each code calls a method on the return value of another method call. This deeply nested model goes against the Law of Demeter, a design guideline to promote loose coupling of data structures that are not closely related and thus should not be coupled.

Remember, less is more. The more code you write into production, the more you will need to maintain it, and the more skeptical you should be about how readable and bug-free it really is.

5. Listen to the User

Yes. By users, I mean real users who will be using the application and not testers who certified your code.

You might argue here.” The tester certified my code. Why should I bother?” Wrong. You are responsible for delivering correct code that will satisfy users, and you need to infer statistics from their feedback to assess the stability or instability of your code.

That said, most bugs result from either modified or new code. Finding a bug in an already stable code that works well in production is infrequent. But it does happen at times.

And how do you know that some code works well for a long time in production? Simply by listening to users. If they did not report problems on some features for a long time, you could be confident that the underlying code is stable.

Remember, real users will complain when they find a bug and remain silent when they consider that the product is working fine. This can be your litmus test as a developer to improve yourself after every such feedback. Some of the most valuable feedback can also be used to address this.

  • Functional issues in code.
  • Missing validation and corner cases
  • API usage and design patterns

And so on.

Your best bet is to strive for excellence, not perfection, and realize that you will only achieve bug-free development if you build a space shuttle.

Your best bet is to strive for excellence, not perfection, and realize that you will only achieve bug-free development if you build a space shuttle.

Concluding Thoughts

Nothing in this world is perfect, including software. It requires rigorous testing before launch and constant updates after launch to stay relevant and ensure a good user experience.

Even after the software is released, you cannot control its execution environment because there are many devices it can run on, and failure conditions can happen anywhere, anytime. Do not fret over it.

Your best bet is to strive for excellence, not perfection, and realize that you will only achieve bug-free development if you build a space shuttle. Excellence focuses your attention on what is right and working rather than on what is not working. Excellence is limitless, progressive, and always rewarding.

As Rick Pitino has rightly said.

“Excellence is the unlimited ability to improve the quality of what you have to offer.”

Sources

  • Writing Solid Code: Development Philosophies for Writing Bug-Free Programs-Steve Maguire
  • Test-Driven Development with C++: A simple guide to writing bug-free Agile code-Abdul Wahid Tanner
  • Clean Code: A Handbook of Agile Software Craftsmanship-Robert C. Martin
  • Timeless Laws of Software Development-Jerry Fitzpatrick
  • Complete Guide to Test Automation: Techniques, Practices, and Patterns for Building and Maintaining Effective Software Projects-Arnon Axelrod

This content is accurate and true to the best of the author’s knowledge and is not meant to substitute for formal and individualized advice from a qualified professional.

© 2022 Ravi Rajan