In the previous installment, I provided a preamble to outline what elements Defensive Programming encompasses. It is a lot more than the general coding mainstream typically associates with it. Some of the key tenets of Defensive Programming are readability, maintainability, compatibility, debugability, code safety, and security. Because they are the foundation for the techniques I will cover later on, please allow me to first explain why these tenets are so important.
What is Readability?
Every day we spend more time reading and re-reading existing code than we do writing new code. It’s to find the location where some new code should go, to try and locate an error, to expand functionality, or to get a refresher on a function we wrote weeks ago. Code reading is one of the most important abilities for any software engineer—and the most overlooked.
Just like a book has paragraphs to create a context that helps readers intuit the flow of the narrative, programmers should do the same for their source code. Readability is largely just the visual layout of your source code. Yet, it can be crucial to preventing bugs. It has the power to lay bare the information and potential side effects in the implementation.
Screen real estate is plentiful these days. High resolution, widescreen displays are everywhere and most programmers use multiple monitors at the same time to make room for all the information they need to keep an eye on. So why would you compress the source code layout to walls of text that are hard to separate, read and ingest?
Just take a look at these two screenshots and decide which one shows its flow and structure better. Which one is easier to grab at a glance? Which one is more pleasing to the eye? The left one is the developer’s version, the right one, I quickly reworked to increase readability. I think the images speak for themselves.
Keep logical constructs together as a block. Separate logical blocks with blank lines, creating white space. This allows the reader to quickly grasp concepts and their place within the source structure. Oftentimes, I will even put braces around code blocks to self-contain a block. A welcome side-effect of it is that it also limits the scope of local variables. Make sure to surround operators with spaces as well, to make the code less dense. That way, the eye can pick up terms and idioms more easily.
Make indentations meaningful
Proper use of indentation is also crucial, even in languages that do not enforce indentation. It creates a visual layout of your data flow that can be grasped very quickly without having to read the full implementation details.
How large should indentations be? Should you use tabs or spaces to create the indentation? This has a lot to do with personal preference and the project circumstances. I recommend using 4 spaces for an indent to create a clearly-visible indentation and I highly recommend using spaces instead of tabs. Since many text editors, such as VSCode, allow you to automatically enforce spaces instead of tabs, automatically converting them as you write, this is very easy to do. The reason I prefer spaces over tabs has to do with portability. If you open a tabbed source code file but your tab spacing is different from the original author’s, you will most likely end up with a garbled layout as tab indentations intermingle with spaced indentations.
As a result, you will end up either cleaning up the code—which is very bad because it can lead to accidental deletions and other bugs—or you will try to decipher the structure of the source—which is exactly what we’re trying to avoid by using defensive programming techniques.
Moreover, some languages will differentiate between tab and space indentations and may create truly bizarre and hard-to-find syntax or runtime errors as a result. These are the kinds of problems we try to avoid as defensive programmers.
The only situation where tabs make more sense than spaces is if you are working with a Braille terminal. For a blind person, it is undoubtedly easier to read 4 tabs instead of 16 space characters. So, if there is a likelihood that a visually-impaired programmer requires access to your source, using tabs instead of spaces is a worthy consideration. You can very quickly replace instances of repeat spaces with tabs if the situation should ever occur. Therefore, using tabs prophylactically does not seem warranted to me in most circumstances.
The establishing of certain programming guidelines is equally important—and make sure every member of your team is adhering to them. It is only through consistency that you can improve code readability. When we visually scan code, our brain trains itself to look for occurrences of certain patterns. Constructs that break with the coding guidelines will interrupt these patterns and you will spend unnecessary time getting over the distraction. Your brain will try to figure out why the code looks different and read its implementation when a glimpse should have sufficed.
Readability makes it possible for every programmer in your team to quickly jump into any other team member’s code and familiarize themselves with it.
Finally, comments are an integral part of readability as well. Constraint and balance are required here because too many comments will bloat the code and might even impede readability, while too little commenting can result in obscure implementations that no one understands. Use common sense when commenting. Don’t state the obvious but rather explain what your code is doing in plain English.
Instead of interspersing comments, I am a big proponent of having comments next to the code. I will place them on the right-hand side, all aligned on the same column. I even considered writing a VSCode extension that will allow you to lock comments to a certain column even if you indent the code block. As I mentioned before, most of us use widescreen displays these days, so make use of the space. Having individual lines of comments interspersed with the code interrupts the flow.
Having a visual column of comments, on the other hand, gives you the ability to ignore the code altogether. You can easily read only the comments to get a sense of what the code is doing without having to deal with any implementational details at all. Just take a look at the screenshot below to see what I mean. The right green column allows you to easily read and understand the function without having to read the actual implementation.
The use of templates can also dramatically affect code readability. While extremely powerful and useful in places, templates tend to create code that is inherently wordy and hard to read. To make matters worse, they are frequently overused to an extreme, so make sure to use templates judiciously. Ask yourself whether a template is really in order. Many times, you’ll find that the problem you’re trying to solve could perhaps be better addressed using polymorphism.
While we are writing software, we typically don’t think about what will happen to our code once we are done. It is easy to forget that after a project is complete, it has a lifecycle. During this lifecycle, the code is frequently updated. Whether it is to fix bugs, improve behavior or performance, or add new features, code typically does not lie fallow. You may even pick it up and refactor it for a different project, or you may leave the project and someone else will have to step in and work through your code.
Either way, your code will most likely be revisited, and we need to ensure the code maintenance will not result in bugs. Our intention as Defensive Programmers should always be to make sure our code can be safely maintained and managed. After all, the job of maintaining it might fall upon you.
There is a lot you can do to help your future self, or others, to understand your code. Avoid tricky constructs that obscure their meaning. A for-loop header that declares countless variables, a string of exit conditions, and numerous variable adjustments in each iteration is a defect waiting to happen—and for no good reason. In a case like this, you can easily declare the variables outside the loop and increment variables inside the loop. The resulting code will properly expose its function instead of hiding it all in a long, convoluted expression that is hard to decipher and therefore prone to errors.
Remember this line from the screenshot above? Brrr… it should make you shudder!
for(k=0;j<look->m && k<b->dim;k++,j++)lsp[j]+=last;
In the same vein, don’t use some arcane tricks or gimmicks in your code. When it comes to coding, being concise, clean, and clear is a much more desirable trait than being the guy who creates the mysterious code that no one understands. Be a team player! And if that is no incentive, make it easy for yourself. It is no fun to look at a piece of code you wrote a few months ago, only to realize that you no longer understand what it is doing because it uses some arcane trickery that you had read about somewhere on the Internet.
If you have to use it, let’s say for performance reasons, at least explain it. If the explanation is too expansive, put a link to an explanation of the algorithm in your comments.
What represents a trick, then? Would you consider a bit-shift standing in for a multiplication a trick? Is a swap using XOR statements to avoid a temporary variable a gimmick? The answer often lies in your audience and their interpretation. The real question to ask, however, should always remain, does it serve a real purpose?
This is it for this time. In the next installment, I will discuss compilers and how you can make these monolithic monsters your friend. Until then, I have a question for you to ponder. Think about it in terms of a Defensive Programming mindset.
Why is it important not to suppress warnings for unused functions or unused variables?
Thanks for stopping by. Preparing content such as the one you have just read takes time and effort to prepare. If you enjoyed it and you are using a Brave browser, please feel free to leave a small tip as a sign of your support by clicking on the small BAT icon at the top of your browser window. Your tip is much appreciated and it encourages me to continue providing more content such as this.