My discussion of failing fast as a development philosophy was relatively general in terms of the level to which it applied. It could be read as a suggestion that it is only inputs to a system that should be validated, but this would be inaccurate. It is my opinion that failing fast is a concept that should be applied at all levels of a system. One key area is in validation in methods.
There are a number of advantages to methods performing validation before execution. The reasons for failing fast I discussed in my previous post. Validation also has advantages to a method in terms of responsibility. By performing validation a method can make it clear that the responsibility for a failure lies with its caller. This seems like buck passing but it serves a valid purpose.
If a method is called by something that violates its preconditions then the fault lies with the caller. Having the method fail fast and state this explicitly makes the error easier to identify. In defect resolution the source of an error needs to be identified so that it may be fixed, not so that blame can be attributed.
There is an attitude that validation is generally not required for methods called by your own code. After all, you wrote both, so you know what the method needs. Validating it is useless overhead. This fails for a number of reasons:
- In team environments others may invoke your code and may not understand all your preconditions, especially if you do not make them explicit.
- Code written by you six months ago may as well have been written by someone else. Good luck remembering all the preconditions. This period may drop to six minutes if you're distracted.
- Your code has bugs. Really, it does. Do what you can to protect against them.
Now that I've convinced you to apply validation in your methods (I'm going to assume that I have), there are two primary types of validation your method should perform.
Firstly a method should do is validate its arguments. Typical checks include ensuring that required arguments are not null and that values are within legal ranges. These validations are to check that the arguments are generally legal, even if they may not be legal given the current system state. They generally result in an exception because the caller is not meeting the requirements of the method. You can skip this step for methods without arguments.
Secondly a method should then validate that it is not being called inappropriately. Examples of checks at this point include method calls on disposed objects and attempting to remove an item from a collection when it is not in that collection. These checks go beyond validating that the argument are in legal ranges and check that the method invocation and arguments passed are valid in context. Where there is no state these checks are generally not necessary.
Combined these validations go a long way to protecting methods from their callers. This is not a cure-all, it is still possible for callers to use the methods incorrectly in ways that cannot be validated against. The validations should therefore form part of an overall strategy of protection against defects.