As I pointed out in my previous post, making your intentions clear is crucial in defensive programming. You never want anyone to guess what your code does. Instead, without ambiguity, you want to make sure everyone understands what your code doing.
Here is another example of a coding practice that I see everywhere.
int newValue = value >> 3;
Most seasoned programmers probably know immediately what is being done here. A bit-shift to the right by 3 is the same as a division by 8. Only, it does not use a divide instruction and instead uses a much faster operation to achieve the same thing. And just like that, a bad habit was born, way back in the 8-bit days, still percolating down to programmers in 2022.
Why then is using the bit-shift a bad habit? The answer is simple. It is cryptic and unclear. While many programmers may immediately recognize this as a pattern, more junior programmers will most likely not. If you never worked on a performance-strapped platform why would you be familiar with this trick? If you never wrote performance-sensitive code, why would you try to optimize a multiplication with a trick?
Using a bit-shift to divide by 2, 4, 8, 16, 32, 64, etc. is an old-school trick. It did make a difference back in the day, but that is no longer the case. Every compiler in the world knows that these divisions and multiplications can be substituted with a bit shift. It will do so for you. Furthermore, the compiler can even optimize other divisions/multiplications as well by making the most of the given processor’s and hardware architecture’s capabilities.
It may seem trivial but being able to read code without cryptic constructs is incredibly valuable. What seems intuitive to you, may not be obvious to others.
There is absolutely no reason why you should not write the expression above like this instead.
int newValue = value / 8;
It is concise and makes its intention clear. There are no drawbacks and it puts the power of optimization firmly into the compiler’s hands where it belongs. As I mentioned in one of the earlier installments, your job is not to outsmart the compiler but to write the best implementation possible.
However, even as a division, something is still off with this line of code. Something that programmers frequently ignore. What is
8? What does it stand for? While practicing defensive coding habits, make sure you also try to avoid meaningless number representations in your code. If
8 stands for the number if images in a row, for example, create a proper constant for it and use that instead. Now, everyone knows perfectly well what the value is divided by, and why.
const int imagesPerRow = 8;
int newValue = value / imagesPerRow;
Not only is the code more readable and meaningful now, but it is also more maintainable. If you ever have to change the number of images per row, it’s an easy and safe thing to do, because all you need to do is modify the constant
imagesPerRow. Defensive coding is forward-looking coding!
Make your intentions clear
When it comes to bit-shifts, as a rule of thumb, simply apply the following thinking to your defensive coding mindset—limit bit-shift operations strictly to bitwise manipulations. If, on the other hand, you intend to perform an arithmetic operation, use a division or multiplication. It makes your code more readable and makes your intentions clear. The compiler will do the rest.
Another example of a bad habit that permeates virtually all code looks something like this…
bool flag = true;
if ( flag ) if ( !flag )
If you put aside your fear of change for a moment, you will agree with me that these lines do not express anything truly meaningful. In C-style languages, the ability to treat a value as if it were a condition has been ingrained in programmers since its dawn and has been perpetuated through the ages. However, if you compare it to these lines of code, it instantly becomes obvious that they make their intentions much, much clearer.
bool flag = true;
if ( true == flag ) if ( false == flag )
A few extra keystrokes turned these expressions into proper comparisons with a sensible condition check. There is no mistaking here what the program is doing. It is easy to read and easy to understand. The meaning is clear. It also expresses that the statement was not an accident and you, perhaps, forgot or accidentally deleted the actual comparison part. With such a subtle change, you clarified all of that. The icing on the cake is that, once again, the compiler-generated code is identical to the original version.
There is no reason to forego the safety, clarity, and meaningfulness of the second version just to save a few keystrokes. It is, in fact, the reason why Java enforces proper comparisons and does not let you get away with sloppy, implied comparisons.
This brings us to yet another example of truly abysmal programming habits. The abuse of booleans. As a data type, a
boolean is a type that can have one of two truth-states,
This means they are not 0 or 1. They are
false and they should be treated as such exclusively. I am fully aware that at its lowest level, booleans are probably implemented as 0 and 1 in your programming language but that should not give you the liberty to treat them as such. Remember the “hide the implementation” tenet of object-oriented programming? It applies here. You do not need to know, and you should not care, how booleans are implemented on the processor level. To you, it should always be either
Do not use a boolean type as a representation of a number in an arithmetic expression. It is a bad habit. Moreover, it is simply wrong because as I pointed out, a boolean is a truth-state without value.
Developers have abused booleans for decades and everyone does it, so why make a fuss about it? What you may not know is that there is absolutely nothing in any language that says that the actual representation of a boolean truth state has to be 0 or 1. Forth-83 and UCSD Pascal, both changed the underlying implementation of booleans in the past—and it wreaked havoc on the existing codebase. Don’t let that be you!
There is absolutely nothing preventing C, C++, Python, Java, or C# from doing the same thing and at some point deciding that
true is no longer
false is no longer
0. Imagine for a moment what that would do to your code. If you are a Defensive Programmer who religiously used
false only, you won’t have to worry about a thing. Everyone else… good luck cleaning up the mess, debugging your code, and explaining to the world what went wrong.
Do not abuse your programming language!
What this ultimately boils down to, just like bit-shifts and proper comparisons, is this. Do not abuse your programming language!
Just because the language allows you to do something a certain way, does not mean you should. Think of the consequences. Sure, you can wrap a car’s safety belt around your wrists instead of your torso and hope for the best. Much good it would do in the case of an accident.
A Defensive Programmer thinks ahead. Sees potential problems lurking and avoids them. Who cares about a few extra keystrokes when the potential payoff is so enormous that it can save an entire project from collapse?
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 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.