Unity Software Design – Defensive Coding


Fourth in a series of posts on Software Design for Unity. Read the introduction here.

Defensive coding is the practice of making your code error-resistant. The idea is to make your code handle unexpected circumstances gracefully, rather than just going wrong or crashing. It’s a pretty broad topic, but in its simplest form means checking for invalid values or references before attempting to do things.

For example, consider the following.

public void FollowTarget(Transform target){
	transform.position = target.position - (target.forward * 1.5f);
}

This method will attempt to move the object 1.5 metres behind another object. The code will compile fine – but if target happens to be null at runtime, Unity will throw a NullReferenceException. It can’t access the position or forward properties of a Transform that doesn’t exist.

The “defensive” method to get around this is simple – check if target is null before operating.

public void FollowTarget(Transform target){
	if (target == null) return;
	transform.position = target.position - (target.forward * 1.5f);
}

Now, even if target is null, the program will continue without exceptions. Sounds good, right? Well, in some cases it is. But as I’ve said a few times now, all techniques can be used and mis-used.

The problem here is that if target is null, the method will return successfully but not actually do what you thought it would. It’s done nothing, and not told you so.

This is fine – IF that’s expected behaviour. If the rest of your code assumes that target may well not exist, and can react accordingly, cool. If not, then your game will behave in an unexpected way, and it’ll be up to you to track down why. In other words, this null check could just be masking a bug further up the chain. Should the thing that calls FollowTarget be passing it a null reference? Or has something gone wrong for it to do that?

Fail Faster, Fail Harder

This potential for “masking” bigger problems is, in my opinion, the fundamental issue with the blind application of defensive coding. In most cases, you should be aiming to highlight, not hide, unexpected behaviour.

A simple modification to the above code reverses its “defensiveness”, but makes it far easier to debug if and when an invalid reference is passed to it.

public void FollowTarget(Transform target){
	if (target == null){
		throw new System.NullReferenceException(“Null target passed to FollowTarget”);
	}	
	transform.position = target.position - (target.forward * 1.5f);
}

This still explicitly plans for the case of target being null, but also explicitly causes the program to fail, with a useful error message. Arguably, given a stack trace and the regular NullReferenceException that would already be thrown, it’s pretty easy to track down what’s happening. But as always, this is a simplified example. In more complex situations, explicitly planning for potentially invalid values and behaviour, and giving clear error output when they occur, can drastically reduce debugging time.

Make your code fail as soon and as hard as possible, so you can notice and fix the problem

The temptation is always to make code fail silently, to make the problem just go away. But the better approach in most situations is to make your code fail as soon and as hard as possible, so you can notice and fix the problem immediately. Band-aids lead to complacency, which leads to users hitting the bugs you didn’t[1]Which is not to say that your released game won’t have bugs. It will. But you can at least increase your chances of finding more of them first!.

When to be Defensive

As ever, this is not intended as a blanket dismissal of this technique. The key is to know when to use it. There are a couple of broad categories of when to use it. The main one is user interaction. A classic example is writing a command line program which asks the user to type in a number, for instance to calculate something.

The program has no control over what the user does with the keyboard, but should be able to handle as many unexpected inputs as possible. Without defensive coding, if the user types letters, the program will either attempt to use the letters as numbers (giving meaningless output) or fail entirely. Better, then, to check whether the user’s input is invalid, and if so, prompt them to re-enter it. Then the calculation is only carried out when the program knows the input is valid, and the program doesn’t just crash on the user.

The other category is when it’s expected, and fine, that things might be invalid. For instance, if you’re making a space sim and your homing missiles search for a target, they very well may not find one! In this case, obviously you should check whether a target exists before homing onto it, and obviously the program shouldn’t throw exceptions[2]In a sense, this particular example can actually be seen as part of the “user interaction” category. The program doesn’t control when a user fires a homing missile, so doesn’t know if there will always be a valid target to lock onto when they do..

In general, you should not defend against your own code. If one part of your code interacts with another part in an unexpected way, you’ve probably done something wrong. You should not leave it to the user to discover this! By causing your code to fail obviously and with clear error output, you can – hopefully quickly – root out such problems and solve them properly.

Footnotes   [ + ]

1. Which is not to say that your released game won’t have bugs. It will. But you can at least increase your chances of finding more of them first!
2. In a sense, this particular example can actually be seen as part of the “user interaction” category. The program doesn’t control when a user fires a homing missile, so doesn’t know if there will always be a valid target to lock onto when they do.


2 Comments

  1. Hi, first thank you for this series of post, since I read the first one I am always waiting for the next, I am trying always to improve my skills, do you know some good reference book or have you learned all this by experience? Also I think you should send this to gamasutra or develop-online for example, I think it is useful not only for Unity developers
    • Thank you! Glad you're finding it helpful :) I can't really point to any one book to be honest, although this is a good broad reference for game-related design patterns. While I've learned from lots of things - formal training, reading, working with others etc - the focus of this series is really about my experience of how to apply theory to real life. So it's less about formalities and more about general best practices to help people get started, and maybe learn from mistakes I've made :)

Leave a Reply to Luke Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>