Sponsored By

5 Effective Strategies You Should Know to Avoid Debugging

If you want to deliver code on time and budget, you must figure out how to reduce the need for debugging.

Jacob Beningo

August 9, 2023

5 Min Read
debugging-GettyImages-128315879.jpg
LUVLIMAGE/The Image Bank via Getty Images

Time and time again, there is one activity that always cannibalizes an embedded software project's financial and delivery budget: debugging. The average embedded software developer spends 20 – 40% of their time debugging code! That’s a lot of time spent fixing what wasn’t done right the first time. I know it sounds harsh, and you might have a different perspective than I do, but if you want to deliver something on time and budget, you must know how to avoid spending time debugging. With how complex systems are today, you might suggest this is unavoidable. In today’s post, we’ll explore five strategies you and your team can use to avoid debugging. 

Strategy #1: Use Test-Driven Development

Test-Driven Development is an Agile technique where a developer uses tests to drive code development. Developers create a list of tests that are required to test a feature. With the list in hand, they implement a test and then write the code to make it pass. Once this is done, they move on to the next test. The process is known as the TDD cycle, based on Kent Beck’s description in Test-Driven Development. The TDD cycle involves the following steps:

  1. Add a small test.

  2. Run all the tests and see the new one fail, maybe not even compile.

  3. Make the small changes needed to pass the test.

  4. Run all the tests and see the new one pass.

  5. Refactor to remove duplication and improve expressiveness.

The goal is that no production code is written without a test in place to verify it. Writing code to make a matching test pass decreases the need for debugging. In fact, if there is a bug, you’ll catch it immediately! The benefit is that bugs don’t stack up, waiting to be found and resolved later in the development cycle when the developer has already forgotten that code area.

Strategy #2: Trace Code Execution Often

One area where teams spend a lot of effort in debugging isn’t necessarily in feature implementation but in system performance. Teams will often write and integrate code quickly. During this process, they may not measure their system performance. After all, early code won’t show performance issues because it can access all the processors' bandwidth. As features are added, the system becomes sluggish or may fail to meet its real-time performance requirements.

The strategy to prevent performance issues in your system is to periodically, or if possible, constantly measure and monitor your system performance. Trace tools will allow you to spot potential issues early and resolve them before they become major. On several occasions, I’ve seen teams think they were close to shipping their product because, from a human viewpoint, it looked like everything was working great. However, when they started to measure performance, they discovered their system was missing deadlines, and the appearance of a working system was just an illusion. Set up a trace tool early and use it often!

Strategy #3: Code Reviews/Pair Programming

I’m not sure about you, but I always seem to misinterpret what people tell me. Communication can be challenging. When converting what you think the customer wants (requirement) into software, getting a second set of eyes on it is critical. You can follow several different strategies to ensure that the translation occurs appropriately.

First, you can use a code review, which is a formal process where your colleagues review your code and ensure it does what the customer expects. While this will ensure that the code does what you and your team think, I’d recommend getting the feature in front of the customer as soon as possible. Customer verification is essential; sometimes, a customer won’t know whether something meets their requirement until they can see it and use it. Finally, when you write code, do so with a colleague. Two sets of eyes are always better than one. We all have diverse and unique backgrounds that allow us to bring a different viewpoint to a conversation and our code.

Strategy #4: Use Assertions

When a bug does occur in your system, it can often be challenging to identify the cause. You usually must step through your code and try to isolate where the problem is occurring. One strategy you can employ to avoid this as much as possible is to use assertions. An assertion is a check at a point in your code that verifies that your assumptions at that point in the code are correct. If the assertion is not true, then that is a bug!

There are many uses for assertions in software. For example, a common place to use assertions is to check input and output conditions in a function. If you have a function that specifies the input range is zero to one hundred, and the user of the function passes in two hundred, that’s a bug! The user did not meet the contract for that function. Instead of allowing the code to continue executing, the assertion can either stop the code execution in its tracks or log the issue.

Strategy #5: Implement Logging

Earlier, we discussed how tracing your system can help you avoid debugging system performance issues. Logging can help you verify that your system is working as expected, but it can also act as a trace to help you understand what led your system to misbehave. Interestingly enough, many logging systems use assertions to log the information! You can choose what information you’d like to log, such as system start-up data, state data, input/output messages, etc.

It is important to note that while logging can be very useful, a balance must be struck with it. Excessive logging can lead to performance issues, storage problems, and difficulty sifting through a large amount of data. If you try logging, start with what you consider mission-critical and indispensable data, then add on.

Conclusions About Avoiding Debugging

If you aren’t careful, debugging software can easily consume your development cycle. In this post, we’ve explored several strategies you can use to avoid debugging. These strategies are the low-hanging fruits that can be implemented with the least effort but provide the greatest return. Implementing these practices can reduce the amount of time spent debugging and improve the overall quality of your code, making it more robust, readable, and maintainable.

About the Author(s)

Jacob Beningo

Jacob Beningo is an embedded software consultant who currently works with clients in more than a dozen countries to dramatically transform their businesses by improving product quality, cost and time to market. He has published more than 300 articles on embedded software development techniques, has published several books, is a sought-after speaker and technical trainer and holds three degrees which include a Masters of Engineering from the University of Michigan.

Sign up for the Design News Daily newsletter.

You May Also Like