📜 ⬆️ ⬇️

Code contracts vs incoming data validation

The validation rules for incoming data are often mistaken for contracts in the code (as, indeed, vice versa). Let's see what is the difference between these two concepts and in what cases they are applicable.

Code contracts: preconditions


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

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

So what are contract preconditions and how do they differ from validating incoming data? The key point here is that a violation of the precondition always indicates the presence of a bug in the client code . Invalid input data, on the other hand, are not an error in the system operation.
')
This is the main difference, all others are the result of it. Let's look at them in more detail.

Differences between contract preconditions and validation of incoming data


Contract preconditions can be thought of as a shield placed inside your code to make sure everything goes well. On the other hand, validation of incoming data is a screen placed for “defense” against the outside world:

image


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

If a red signal appears inside your system, this means that either you have not filtered 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 eliminated as quickly as possible. And this is where contracts help us. They allow you to stop the distribution of invalid data on the application and quickly identify the cause of bugs:

image


When your application has a good set of preconditions for contracts (and, better yet, postconditions and invariants), invalid interactions are localized and their distribution stops as soon as they occur, which makes debugging the application very easy.

Validation of incoming data is a mechanism designed to protect your application from the entry of incorrect data from the outside. This mechanism makes no assumptions about the data with which it works . This means that the data may be invalid, and this in itself is a valid situation.

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

On the other hand, preconditions for 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 preconditions: best practices


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

This leads to the following characteristics, which must be followed by each precondition of the contract:


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

The second item means that the service class should not force the client class developer to perform complex checks in order to satisfy the contract. If the verification algorithm is complicated, it should be put in a separate method that customers 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 item speaks of the repeatability of the test result. If class preconditions depend on the external environment — for example, the existence of a file on disk — clients will not be able to successfully complete 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 the verification of the contract, and the client cannot do anything to avoid it. Such verification is not a precondition for contact, since it does not indicate the presence of a bug in the client code , so we cannot enter it as a precondition.

Conclusion


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

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

Source: https://habr.com/ru/post/264113/


All Articles