Defensive Programming, or a study on how to become a better software engineer
So, recently I volunteered to make a presentation about defensive programming at work, and I realized that there isn't much sorted info on this subject on the internet.
Also, I found out that this is a really divisive subject among experts, due to its risk of becoming a burden and not a helper, and there's a lot of contraditory infos and/or definitions about it. For someone that is starting to learn a bit more about the theme, it can be a bit overwhelming.
So, I'd like to share a little bit of my study/point of view about this subject, in the hope that it helps someone that wants to learn a bit more about this subject that is near and dear to me.
So, what is defensive programming in the first place?
According to Wikipedia, here's the definition:
“Defensive programming is a form of defensive design intended to ensure the continuing function of a piece of software under unforeseen circumstances. Defensive programming practices are often used where high availability, safety, or security is needed.”
While this definition is a great one, and pretty much summarizes what Defensive programming is all about, I find it a bit too theoretical and not clear for the application of the concept in practice.
What are the boundaries of unforeseen circunstances? How do you translate that to real world scenarios? It should be only used on critical software?
On my research, I've came upon this definition of Defensive Coding by George Schlossnagle:
“Defensive coding is the practice of eliminating assumptions in the code, especially when it comes to handling information in and out of other routines.”
I like George's point of view on Defensive Coding, because for me, it really is about this. Defensive Programming is much more related to being a better coder, eliminating assumptions that you may have when implementing features, and specially paying attention on how your application navigates through its functionalities.
Friend or foe?
So, if you try to research about the theme around the internet, you'll find a lot of scattered and divergent opinions about the theme. Many people consider defensive programming something that can do more harm than good.
The most common arguments is that, if not used correctly, defensive programming can generate a lot of boilerplate code, making it more polluted with unnecessary checks and affecting the readability, therefore, worsening the maintenance.
Also, if the developer is not aware and the team/project does not have a solid or stablished error handling process, the defensive coding can increase the chances of hiding errors you may have in your application, which is far from great.
Besides that, you have specialists, like Robert C. Martin (aka Uncle Bob), that aren't great fans of this mindset, as seen in this tweet:
"Defensive programming, in non-public APIs, is a smell, and a symptom, of teams that don’t do TDD."
So. Is defensive programming a friend or a foe?
I'd say friend, here's why:
First of all, I think that defensive programming is more of a mindset than anything. Is you as a developer trying to make your code as safe, solid and organized as possible.
Also, I've found this two quotes by specialists that I'd like to present here. The first one is by David Thomas and Andrew Hunt:
“Much of the time, tomorrow looks a lot like today. But don’t count on it.”
This is a key statement to remember when you're working as a software developer. It is your obligation as an engineer to make your code as sturdy as possible, while also being aware of the future and possible changes that will occur.
When coding defensively, you're preparing your application for being as ready as possible for changes that will for sure happen in the future. Not only that, but by being mindful of having this "layer" of protection, you're making the life of whoever has to maintain that code easier, as it will be much faster to perform the changes and debug, due to the previous preparation.
The next quote is by Jim Bird:
"Defensive programming is like defensive driving, it’s something that everyone can understand and do. It requires discipline and awareness and attention to detail, but it’s something that we all need to do if we want to make the world safe."
I like this quote because it really sums up the idea. Coding defensively is a mindset that anyone can learn, and by applying it, you're making the whole software development subject more mature. It is intrinsically related to Quality and Software Maturity. So it's something every developer should be aware of.
So how does this translates to the real world?
On this article, I'm going to focus mostly on the mindset and cases you should be aware of, but code examples can be found in the links scattered through the page, as well of a list of references I've encountered on my research. If you want to go deeper in the subject, I definitely encourage you to check it out.
According to this brilliant lecture of McGill University, defensive programming can be divided in 3 pillars:
Pillar 1: Never Assume Anything
Never assuming anything is the core of defensive coding. Is you as a developer being aware that you shouldn't not be aware of the info you're handling when building your application.
That means being aware of possible NULLs in your runtime, whether they are information, hardware or memory-wise, having pre conditions you validate before you start manipulating the information, limiting the input entry types by the user or API to the minimum possible and being mindful of your target system/language limits.
Also, a great mindset that applies to this pillar, is wrapping your 3rd party libraries/APIs contracts into local gateways, and building your application based on that. 3rd party code is subject to change, and your code shouldn't be fully based upon it.
It is better to "duplicate" and have a local contract where you centralize this kind of information, therefore being easier to maintain, than to have many parts of your application based on this kind of information you don't have full control.
But I think the greatest tip of this pillar is: Expect the Unexpected and Be prepared for the illogical.
One of the great phrases I've ever listened on my career is that the end-user is creative. When you're building your software you should always remember that. Test not only the success and failure cases, but also the illogical things that your user may do.
Pillar 2: Use and abuse of Code Standards
Coding standards are something defended mostly by everyone in the industry. When you have coding standards set up and aligned with everyone, you mitigate the possibility of having code written in various ways on your project.
Things like having the loose hardcoded values of your code, being magic numbers or not stored in constraints, being mindful of the naming scheme of your project in all levels, from folders to variables, and limiting the usage of global values already are a great help for improving your code.
Besides that, one thing you should be really careful in following is the Single Responsibility Principle (S.R.P.) on your code. Things should be as specific as they can be given their context in your code, and have one purpose only. This is one of the key features of the SOLID guidelines for Software Development, and it really makes sense when you think about defensive programming.
Linked to this, one thing that really helps is having the mindset of making your software as modular as possible. When you apply this in your software, you really make things easier when you have to escalate it or even when specific business needs appear.
One thing you should be really aware of when implementing defensive programming in your project is having a defined process of logging errors. As you will be anticipating situations that may happen during the lifecycle of your application, it is important to not fall into the trap of hiding the errors.
Pillar 3: Keep it simple
This may be the golden rule for this topic, and the simplest one. At least in theory.
The easier you can make to understand your code, the better. Things like keeping your code organized, clean and pronounceable, paying attention to the size of your functions and classes and keeping related things close to each other make a long way for making the life of whomever has to maintain your code easier.
So…that's it?
Yep. At least the core of it that is.
Defensive programming is much more than a set of rules you should follow when coding, for me at least, is much more about the mindset you need to have when developing and designing your solutions than anything else.
Of course you need to follow a set of rules and good practices for having a quality code, that's undeniable. And there are so many tools and books out there that help you with the nitty-gritty of how to things, like Clean Code, Clean Architecture, Refactoring, SOLID, and so on.
But being mindful of why and what are you doing when you apply this concepts on your solutions really opens your mind on predicting problems and constructing a solid solution.
Update:
Wanna know more about the theme and see practical applications of it? Check this amazing article by Peter Lupo:
References/Useful Links
https://www.computerworld.com/article/2563343/tips-on-defensive-coding.html
https://twitter.com/unclebobmartin/status/63993473166086144
https://interrupt.memfault.com/blog/defensive-and-offensive-programming
https://enterprisecraftsmanship.com/posts/defensive-programming/
https://www.cs.mcgill.ca/~adenau/teaching/cs206/lecture18.pdf
https://enterprisecraftsmanship.com/posts/functional-c-primitive-obsession/
https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29
https://dzone.com/articles/defensive-programming-just
https://www.brainstobytes.com/defensive-programming-fundamentals/
https://dev.to/cubiclebuddha/is-defensive-programming-actually-healthy-5flj
https://medium.com/@vcarl/overly-defensive-programming-e7a1b3d234c2