Finagle’s Law of Dynamic Negatives:
Anything that can go wrong, will – at the worst possible moment.
What Does “Defensive Programming” Mean?
Defensive programming, simply put, is programming with the intent to anticipate likely failure points. The goal is to circumvent those likely problems before they occur. You see the problem, right? There’s something inherently difficult with the advice “expect the unexpected” and it’s made many times worse when one alters it to “expect the unexpected and try to prevent it”.
Let’s look at some practical examples.
Conditional Statements
This is one of the easiest places to program defensively, and one where it’s easiest to be complacent. In many cases while programming in PHP, you simply won’t need an “else” case.
Hypothetically, you are working on a function and need a conditional. You only have three possible states for your particular variable at this point – and you have those covered in a block like this:
if($var == a){ } else if($var == b){ } else if($var == c){ }
There are no other possibilities, you say, and you move on with your code. Let me stop you there, though. I know that you know there are no other possibilities. And I believe you. But sometimes (unexpected) things happen. We forget about something. We look over mistakes. We end up reusing a bit of code outside of its originally intended scope. And all of a sudden we have leaking and sometimes silent error states because there’s no catch here. Make use of else
blocks. Make use of default
in switch
es. Use them to return or log an error so that you’re aware of what’s happened, should it ever happen. It may be an extra couple of lines of code, but it’s worth it when something happens that you did not expect.
Never Trust User Input
Have you heard this statement before? Most programmers have. It is a bit of a vague, generalized thing to say, granted. But it’s true. You should never trust user input. This does not mean that you assume that all users are crazed hackers out to destroy your application with a single set of well crafted commands. There is no need for paranoia. But, you should assume that users don’t know your code. They don’t know what parameters you need filled or how long the input can be. They don’t know what file types they can upload (even if the app tells them) or what size. And occasionally, they are a bot or a hacker and they are going to be trying to run scripts in their inputs. Sometimes even from inputs behind a login wall. How do you know when you can trust things like authentication or captchas to provide a safe barrier before users arrive at input forms?
The answer: Never.
If you never trust user input, then you never have a breach because you trusted user input. See? So always validate your input, and always ensure that you’re using appropriate techniques when handling data, especially when storing it in the database or retrieving it for display. For that matter – don’t trust input at all, even when from somewhere besides your users – input validation is always your friend. Check out Survive the Deep End: PHP Security and look into using a validation library.
Assumptions About Your Code
Don’t assume anything. If the previous two topics teach us anything, it’s that we should not make any assumptions. As programmers, especially when focusing on a single project for too long, we begin to assume a lot. We begin to assume that the user knows some of the things we know. Not necessarily technical details, but functional details about the program. We assume that the user will know how big files can be because… we already know that fact. Or that they’ll know that in order for the mailing script to… but no, they have no idea about any of that. This can sometimes matter more for front-end work, but obviously you still deal with user behavior and user input in the back-end as well, so it’s worth thinking about.
Another staggering assumption that many programmers make is the assumption of the obvious nature of our functions, classes, or whatever other bit of code we’re working on at any given time. A defensive programmer will try to think carefully about not only normal documentation and describing what a bit of functionality does – but they will also document any assumptions they are making about input, parameters, use cases, or any number of other similar things. Because we are all humans, and we sometimes forget things later. We also will all most likely end up with someone maintaining, extending, or replacing our code someday. If nothing else, recall that programming is happening in a world full of technological changes. If your application is still around in several years, it may need to be updated to a newer version of PHP and lose some of its functionality, or any number of components that it interacts with may change and necessitate changes in your own code. It is very difficult to predict these things, so good comments and documentation are very important.
Tunnel Vision
Another thing that can cause us to both forget good commenting practices as well as standards is tunnel vision. Tunnel vision happens a lot to programmers. You know the feeling. You’re solving a problem, you’re in the groove. You’re feeling isolated in your own little world, with your music (or lack thereof) and you’re just coding and all of a sudden two hours have passed since you last checked the clock and you realize that you’ve written countless lines of code without any comments. It happens to all of us at one time or another, but the important thing is to, at some point, catch that and add some where appropriate.
Consistency in Syntax and Naming
Consistency is a bit of a grey area – that delves a bit more into coding standards and the like, but it is relevant to defensive programming. In PHP, there are standards that can be followed to streamline your code for others who might view it, or for your own future use. But often, no one is actually making you code to standard. However, whether you’re coding to some set of standards or not, you should at very least be internally consistent – because it will make you less error prone. This applies especially to small syntax errors that take an aggravating amount of time to return to and fix after being caught. If you always use the same spacing, the same formats and syntax, naming conventions, etc., than you are less likely to make a mistake resulting in your misreading of your own code. You are also more likely to be able to quickly scan and find things that you need to find.
Conclusion
Beyond user behaviors and actions, don’t assume anything in general about your program. Assumptions are one of the biggest enemies of a defensive programmer. Don’t assume you won’t need that default case or else statement. Don’t avoid creating appropriate user error messages, alerts, logs, and whatever else you need just because you assume they won’t be needed. Your assumptions are often right – but we do not care about that. What we care about are the times that they are wrong. Always plan as though you may need to return to your code in a few hours, weeks, months or even years, or that someone else will – and document it accordingly. Don’t assume it will never need to be updated, extended, or maintained. That’s naive at best, and negligent in more cases than not. Sometimes just keeping the idea of defensive programming in the back of your mind can help you to estimate, plan, and program more effectively and more safely.