Why can't developers write safe code? We do not speak here again about the
"clean code" . We are talking about more from a purely practical point of view - about the reliability and security of software. Yes, because unsafe software is pretty much useless. Let's see what “unsafe” software means:
- Flight 501 of the European Space Agency’s Ariane-5 rocket stopped 40 seconds after launch (June 4, 1996). An experimental prototype rocket worth $ 1 billion was self-destructed due to an error in the on-board control software.
- An error in the program that controlled the Therac-25 radiation therapy unit was the direct cause of death for at least five patients in the 1980s, when she asked excessive doses of X-ray radiation.
- A software error in the Patriot anti-aircraft missile system MIM-104 , which caused its system clock to go one third second in a hundred hours, led to its inability to detect and intercept a flying missile. An Iraqi rocket hit a military unit in Dhahran, Saudi Arabia (February 25, 1991), killing 28 Americans .
This should be enough to understand how important it is to write safe and properly working software, especially for certain applications. But in other cases of use, we need to know what our program errors can lead to.
First acquaintance with defensive programming (designing self-testing and self-correcting programs)
Why do I think defensive (defensive) programming is a good approach to solving these problems in a certain kind of projects?
Protect yourself from the impossible, because that is what will happen.
There are many definitions for the term "defensive (defensive) programming"; they depend on the level of
“security” and the level of resources required for your software projects.
Defensive programming is a form of defensive (defensive) design designed to ensure the continuous operation of a certain part of software in unforeseen circumstances. Security programming techniques are often used where high availability, security, and reliability are required ( Wikipedia ).
I personally think that this approach is expedient when there is a large and long-lived project in which many people are involved. It is also suitable, for example, for such an open source project, which requires substantial and continuous maintenance.
Let's look at some of my simplified key points to achieve a defensive programming approach.
')
Never trust user input
Assume always that you can get what you do not expect. This should be your approach as a programmer involved in defensive programming in relation to user input or, in general, to everything that goes into your system. Because, as mentioned above, the impossible is possible. Try to be as strict as possible.
Prove that your input values ​​are what you expect.
Best defense is attackDo white, not black lists, for example, when checking the image extension, do not check for invalid types, but check for valid types, excluding everything else. For example, in the PHP language, you also have a lot of open source verification libraries that simplify your work.
Best defense is attack. Be strict.
Use an abstract database view
The first of the
top 10 software vulnerabilities according to OWASP are injections. This means that someone (or many people) does not yet use secure tools to access their databases. Use packages and libraries of abstract database representation. In PHP, you can use
PDO (Portable Distributed Object) to
provide basic protection against injections .
Do not reinvent the wheel
Are you not using a framework (or microformrvk)? Well, it means you like to do extra work without any need, congratulations! This applies not only to frameworks, but also to many other new features, where you can easily
use what has already been done, well tested, what thousands of developers trust and that works steadily ; It is not worthwhile to engage in a demonstration of your skill only for the love of one. The only reason to do something yourself is the need to get something that does not exist or that exists, but does not meet your requirements (poor operating parameters, missing characteristics, etc.).
This is what is called
intelligent code reuse . Take advantage of this opportunity.
Do not trust the developers
Protective programming is akin to what motorists call
“cautious” driving . When “careful” driving, we assume that everyone around us may act improperly. Thus, we must be careful, carefully tracking the behavior of others. The same applies
to defensive programming when we, as developers, must not trust the code of other developers . Nor should we trust our own code.
In large projects involving many people, there can be many different ways of writing and organizing programs. This can create confusion and generate errors. That is why we must apply development styles and a code quality controller that simplify our lives.
Write a reliable code
This is a difficult thing for a (defensive) programmer to
write the proper code . Many people know and talk about this, but in reality few people care or pay enough attention and effort to get a
RELIABLE code .
Consider a few examples of inappropriate approach.
Do not do this: uninitialized properties
<?php class BankAccount { protected $currency = null; public function setCurrency($currency) { ... } public function payTo(Account $to, $amount) {
In this case, you need to remember that in order to make a payment, you must first call
setCurrency . This is indeed a bad approach — a state transition operation, such as indicated (making a payment), should not be carried out in two stages using
two (or more) public methods. You can have many payment methods, but
we are obliged to have only one simple public method for changing status (it is absolutely unacceptable to have objects in an uncertain state) .
In this case, we did even better by encapsulating an uninitialized property into a
Money object:
<?php class BankAccount { public function payTo(Account $to, Money $money) { ... } } $bankAccount->payTo($joe, new Money(100, new Currency('GBP')));
Protect the program from abuse.
Do not use uninitialized object properties.Do not do this: a leaky state outside the class scope <?php class Message { protected $content; public function setContent($content) { $this->content = $content; } } class Mailer { protected $message; public function __construct(Message $message) { $this->message = $message; } public function sendMessage(){ var_dump($this->message); } } $message = new Message(); $message->setContent("bob message"); $joeMailer = new Mailer($message); $message->setContent("joe message"); $bobMailer = new Mailer($message); $joeMailer->sendMessage(); $bobMailer->sendMessage();
In this case, the
Message is passed by reference, and the result will be in both cases the
“joe message” (“Joe message”). The solution would be to clone the message object in the Mailer constructor. But what we should always try to do is use a (
immutable )
value object instead of a simple variable
Message object.
Use immutable objects when you can. <?php class Message { protected $content; public function __construct($content) { $this->content = $content; } } class Mailer { protected $message; public function __construct(Message $message) { $this->message = $message; } public function sendMessage( { var_dump($this->message); } } $joeMailer = new Mailer(new Message("bob message")); $bobMailer = new Mailer(new Message("joe message")); $joeMailer->sendMessage(); $bobMailer->sendMessage();
Write tests
Do you still need to talk about this? Writing unit tests will help you withstand general principles, such as
strong connectivity, personal responsibility, loose coupling, and proper composition of objects . This will help test not only a working small module, but also a way of structuring your objects. Indeed, you will see clearly by testing your small functions, how many modules you need to test and how many objects you need to simulate in order to get 100% coverage of code tests.
findings
Hope you enjoyed the article. Remember that here are just suggestions. It is up to you when and where to apply them and whether to apply them at all.
Thanks for reading!