C# code contracts vs input validation

Input validation rules are often taken for code contracts. In this post, I’ll try to cover their differences and show what their common use cases are.

Code contracts: Preconditions

What is the purpose of having a code contract, namely preconditions, invariants and postconditions? The idea is closely related to the Fail Fast principle: the faster you notice an unexpected behavior, the quicker you fix it.

In the vast majority of cases, it is much more efficient to crash the application rather than try to automatically recover from an error because you never know what the error is and how it can affect your application. Allowing an application to continue running after a bug have occurred may corrupt its persistence state.

So, what are contract preconditions and how do they differ from input validation checks? The key point here is that a precondition violation always states that there’s a bug in the client code. Invalid input data, on the other hand, does not indicate a bug in your system.

That is the main point; all the other guidelines and best practices grow from that statement. Let’s dive deeper and try to draw a line between the two.

Code contracts vs input validation: the differences

You can think of contract preconditions as a protective shield placed inside of your code to ensure that everything goes fine. On the other part, input validation is a shield placed to defend you against the outside world:

Input validation vs contract precondition
Input validation vs contract precondition

Red signals represent invalid interactions (or data) coming from users or other applications; green signals stand for valid interactions. Your goal as a developer is to make sure that no invalid data can reach your code.

If a red signal appears to be inside of your system, then either you didn’t filter input data well or your own code generates them in some cases.

In either case, it is a bug and it is better to locate this bug as soon as possible. That’s when code contracts show up. They allow you to stop red signals from spreading across the application and quickly find out the cause of the bug:

Precondition violation
Precondition violation

When your application has a rich set of contract preconditions (and, preferably, postconditions and invariants), invalid interactions are doomed. They are being caged as soon as they appear making it extremely easy to debug and fix the code generating them.

That said, input validation is a mechanism designed to protect your system from invalid data infiltration. Such validation makes no assumptions about the data coming in. That means the data is allowed to be invalid and that itself is a valid situation.

Indeed, if a user enters "Fifteen" in a numeric field, you don’t want your system to crash. Instead, you want it to politely inform the user of the error.

On the contrary, contract preconditions do assume that the data inside of your system is in a correct state. If it’s not then there is a bug in your system.

Contract preconditions: best practices

Now, when I hope we made it clear what is the difference between contract preconditions and input validation, let’s talk about what can be considered as a contract and why.

A code contract is a public agreement proposed by the service code. It says that if the client follows some rules - preconditions - then the service guarantees to provide some results described in postconditions.

That leads to the following attributes every contract precondition should have:

  • They should be public. That is, every client developer is able to get to know them before writing a line of code.

  • They should be easy to check meaning that client developers are not needed to write complex algorithms to emulate them before calling a method.

  • They should not rely on non-public state because that will restrict client code in its ability to check them.

  • They should be stable. That is, a precondition validation result shouldn’t depend on volatile class members.

Let’s go through these points. The first one is quite simple; it means that the preconditions should be somehow described so that the client developer can read them before using the method. The best way to do it is to explicitly specify them at the top of the method’s code:

public string GetName(int position)
{
    Contracts.Require(position, x => x >= 0);
 
    // Rest of the method
}

The second one means that service class shouldn’t make client developers write complex calculations in order to comply with the contract. If you do need a complex contract, provide a separate method that can be used by your clients to check the preconditions:

public int Devote(int amount)
{
    Contracts.Require(() => CanDevote(amount));
 
    return DevoteCore(amount);
}

The next point follows from that one: you can’t make your preconditions depend on non-public methods or state, otherwise your clients won’t be able to comply with them.

The last one stands for contract stability. If a class’s preconditions depend on volatile variables - for example, file existence - then the clients won’t be able to do anything to meet them:

public string ReadFile(string filePath)
{
    Contracts.Require(() => File.Exists(filePath));
 
    // Rest of the method
}

In this code example, file might have been deleted or inaccessible and your client can’t do anything about it. That check is not a contract precondition because it doesn’t mean there’s a bug in the client code. Thus, you shouldn’t introduce it as a precondition.

It is still an exceptional situation and you can use regular exceptions to deal with it:

public string ReadFile(string filePath)
{
    if (!File.Exists(filePath))
        throw new ArgumentException();
 
    // Rest of the method
}

Summary

Contract preconditions and input validation have similar but still different purpose. While input validation ensures that your system is protected from the outside world, contract preconditions guard your code inside of your system and help you quickly locate and fix bugs in it.

Subscribe


I don't post everything on my blog. Don't miss smaller tips and updates. Sign up to my mailing list below.

Comments


comments powered by Disqus