Defensive Programming 101

Defensive Programming 101

For any given programmer the following statement should always be treated as truth:
My code sucks, but your code sucks more.

Good version control habits and test coverage will get you out of most jams related to your own code but we rarely write apps that are comprised of just our code. There are almost always libraries from other people code that you’ll include to save yourself from having to re-invent the wheel. Obviously you don’t want to start writing unit tests for code from other projects (you’d never finish) but there are some basic steps you can take to minimize your chances of failure.

Untrustable code

The fact is that no matter how good your code is you just can’t trust anything you didn’t write. Furthermore, you can’t trust what people will do with their code.

You can’t trust other people’s code because it rarely has good test coverage or documentation so you can’t be sure if it is broken, or if it works the way you think it does. And unless it’s a huge open source project you can generally assume that not many people have looked at it either.

You can’t trust what they will do with their code because it’s their code not your code. Whoever “they” are they’ve got their own ideas about where it’s going next and how to get it there, and they may not jive with how you want to use it.

Your project must never rely on the successful build of another project, because it may depend on the successful build of yet another project, or even worse, end up in a sea of circular dependencies.

The results of another project’s “current” build should similarly not be trusted. It’s probably not tested well, the implementation could change completely tomorrow, and it may just be buggy. Whenever possible only use release builds. If you can’t use a release build at least try and use one that other people have had success with for a little while.

Untrustable people and environments

Even if you take all the steps possible to defend against other people’s bad code there’s still the fact that humans are incredibly fallible.

You can not assume that the correct libraries will be where they’re “supposed to be” or that the version that is there is 100% compatible with the version you coded against. Even if it is you can’t assume that someone won’t go and “upgrade” it to a version that isn’t compatible. Always bring your own libraries and keep them local to your app. Disk space is cheap and unless you’re on an embedded device it’s better to bring along copies of the libraries you coded against. It doesn’t matter if the ones you bring along are completely outdated relative to the latest version; what’s important is that you can guarantee that the version you coded against works.

Your project must never rely on another project or its libraries. Period. You can not assume that the other project will be on every box your project is installed on or that the version on those boxes be in a state that is either working or compatible with yours. Take whatever you need from their project and copy it into yours. If you need libraries that it generates or contains, copy them. If you must use source code copy it but be very careful. You don’t want to end up forking it and having to maintain your own version. Instead, copy it in but do whatever it takes to make it blazingly obvious that the code should not be edited unless absolutely necessary. When you do copy over make sure to copy the smallest amount possible. If you can, build it into a separate library file that you can update when the need arises.

You also can’t assume that someone isn’t going to screw up a config file, and that includes you. Just like libraries, bring your own. Maybe you’re working on a webapp which has some global configuration file that’s shared by other webapps on the same box. Don’t trust it. Yes use it so that you’ll be in sync with the other apps but since you can’t guarantee that some sysadmin won’t accidentally delete or change some piece of it that’s crucial to your app bring along a fallback with some bare bones settings that should work under any circumstance you can prepare for. This is exactly why your operating system has a “safe mode”. The operating system developers know you can’t trust the users not to accidentally screw something up or install an app that screws something up. Have a fallback.

If your app is configured differently for different deployments (maybe you’ve got a dev box, a QA box, and a production box, each with their own different database settings) then have a separate config file for each deployment. Don’t have a single config file with different sections that get commented or uncommented based on which deployment it is. You simply can’t guarantee that you, or your coworkers, won’t forget to comment or uncomment the right bits when deploying.