Readability Beats Debugging

By Colin Walls

Embedded Software Technologist

September 22, 2020

Blog

Readability Beats Debugging

When writing code, your first priority should be readability. A lot of time is spent debugging and maintaining code, often far more than was spent writing it in the first place.

When writing code, your first priority should be readability. A lot of time is spent debugging and maintaining code, often far more than was spent writing it in the first place. So, making that process efficient is essential. Considering the needs of the human reader in the future (who might be you, of course!) is more important than trying to make the code “efficient” – that job can mainly be left to a modern compiler.

This consideration means that the code should be formatted and aligned very carefully, and language constructs should be as simple and transparent as possible. There are many published guidelines that help with these matters. However, creating readable code does not end there.

When you compile code, the programming language is translated into machine instructions. That is obvious. However, what the compiler actually receives is a stream of characters; some is actual code, but there may be chunks that are not intended to be translated and other text may be for human consumption only:

  • Documentation – comments in the code
  • Temporarily removed code – part of the debugging process, but it may persist
  • Special debugging/tracing code

Implementation of each of these has some influence on readability.

Documentation

Everyone knows that comments are a good idea, but most of us get lazy. However, some effort is very worthwhile. The old-style /*...*/ comment notation was acceptable, but the newer end-of-line //... form is clearer. Care is still needed. For example:

int number;  // input count

char c; // single character buffer

char buffer[99];  // the input line

 

is so hard to follow. Alignment is everything:

 

int number;        // input count

char c;            // single character buffer

char buffer[99];   // the input line

And do not use tabs; they are not portable.

Temporary code removal

During software development, it is not uncommon to want to “hide” parts of the code from the compiler – to switch it off. The traditional way to do this was “commenting out” – putting a /* before the code and */ after. Although quick to do, it can easily be ineffective, as comment nesting is not necessarily supported by compilers. The newer // notation is a little better, but it is tedious to apply and remove and can still be error prone.

The best way to achieve this result is by using pre-processor directives, thus:

#if 0

  

#endif

Debug/Trace code

A particular kind of temporarily visible code is instrumentation – extra code added for debugging and/or tracing. Although modern debuggers and tracing tools can do a remarkable job, sometimes instrumenting the code is the only way to glean visibility and figure out exactly what is happening.

The usual way to accommodate this need is using pre-processor directives, as before, but using a symbol to switch them on and off:

#ifdef DEBUG_TRACE

  

#endif

So, when the symbol DEBUG_TRACE is defined, the debug code is included.

A slightly different approach is to code it like this:

#ifndef NDEBUG

  

#endif

Although this double negative does seem confusing, some consistency is introduced, as this symbol is used to enable the standard assert() macro. The symbol needs to be defined to suppress debug mode.

My work in the electronics industry spans nearly 40 years, almost exclusively with embedded software. I began developing software and managing teams of developers.Then, I moved to customer roles, including pre-and-post sales technical support, sales management and marketing. I have presented at numerous conferences, including Design West, Design East, Embedded World, ARM TechCon, and my work frequently appears on Embedded.com.

More from Colin