Code contracts vs input validation

Original author: Vladimir Khorikov
  • Transfer
Validation rules for incoming data are often mistaken for contracts in code (as, in fact, and vice versa). Let's look at the difference between these two concepts and in what cases they are applicable.

Code contracts: preconditions


What is the purpose of using contracts in the code, namely preconditions, postconditions and invariants? The idea is closely related to the Fail-fast principle : the faster you notice unexpected behavior, the faster you will fix it.

In most cases, it is more efficient to let the application crash than to try to automatically recover from an error, because You never know for sure what exactly this error is and how it will affect the application. Continuation of the software after an error in it can lead to an inconsistent state of data in the database.

So what exactly are preconditions of contracts and how do they differ from validation of incoming data? The key point here is that a precondition violation always indicates a bug in the client code. Incorrect incoming data, on the other hand, is not a system error.

This is the main difference, all the rest are a consequence of it. Let's look at them in more detail.

Differences between preconditions of contracts and validation of incoming data


The preconditions of the contracts can be represented as a protective screen placed inside your code in order to make sure that everything is going well. On the other hand, validation of incoming data is a screen placed for “defense” from the outside world:

image


Red signals indicate incorrect interactions (or invalid data) coming from users or other applications. Green signals indicate acceptable interactions. Your goal as a developer is to make sure that invalid melons cannot enter the system.

If a red signal appears inside your system, it means that either you did not filter the incoming data, or your own code generates incorrect data.

In any case, this is a bug in the system that needs to be localized and fixed as quickly as possible. And contracts help us in this. They allow you to stop the spread of invalid data on the application and quickly identify the cause of the bugs:

image


When your application has a good set of preconditions for contracts (and, even better, postconditions and invariants), invalid interactions are localized and their distribution stops as soon as they arise, which greatly facilitates debugging of the application.

Validation of incoming data is a mechanism aimed at protecting your application from the penetration of incorrect data from the outside. This mechanism makes no assumptions about the data with which it works . This means that the data can be invalid, and this in itself is a valid situation.

In fact, if a user enters “ten” in an integer field, you do not want your application to crash. Instead, you politely point out to the user an error.

On the other handpreconditions of contracts make assumptions that the data inside your system is in a valid state . If this is not the case, an error has crept into your application.

Contract Prerequisites: best practices


Let's see what can be considered as a precondition for a contract. A contract is a public offer offered by a service class. It says that if the client follows certain rules (preconditions), then the service guarantees some of the results described in the postconditions.

This leads to the following characteristics that each precondition of the contract must follow:

  • Preconditions must be public. This means that the client class developer must be able to find out about them before starting to write code.
  • They should be easily verifiable. The client developer does not have to write complex algorithms to test them before calling the method.
  • Preconditions should not rely on non-public status, as this limits the customer’s ability to pre-test them.
  • They must be stable. This means that the result of the precondition check should not depend on the external state.

Let's look at these points in more detail. The first is quite simple, it means that all preconditions must be described in some way, so that the developer can familiarize themselves with them before using the class. C # does not yet allow embedding contracts directly in the signature of methods, so the best way at the moment is to describe them at the beginning of the method.

The second point means that the service class should not force the developer of the client class to perform complex checks in order to satisfy the contract. If the verification algorithm is complex, it should be moved to a separate method that clients can use to validate the precondition:

public int Distribute(int amount)
{
    Contract.Requires(CanDistribute(amount));
    return DistributeCore(amount);
}

The next point follows from this: you should not make your preconditions dependent on non-public methods or fields, otherwise clients will not be able to do the appropriate checks before interacting with the class.

The last paragraph speaks of the repeatability of the test result. If the class preconditions depend on the external environment - for example, the existence of a file on disk - clients will not be able to achieve successful execution of the method in 100% of cases:

public string ReadFile(string filePath)
{
    Contract.Requires(File.Exists(filePath));
    // Rest of the method
}

In this example, the file may be deleted or become inaccessible between the call to the ReadFile method and the start of contract verification, and the client cannot do anything to avoid this. Such a check is not a prerequisite for contact, because it does not indicate a bug in the client code , so we cannot introduce it as a precondition.

Conclusion


Contact preconditions and validation of incoming data have a similar, but nonetheless different purpose. While validation of incoming data allows you to make sure that the system is protected from the outside world, preconditions of contracts protect your code inside the system and allow you to quickly localize and eliminate errors in it.

Link to the original article: C # code contracts vs input validation

Also popular now: