27

I recently stumbled upon Microsoft's framework for code contracts.

I read a bit of documentation and found myself constantly asking: "Why would I ever want to do this, as it does not and often cannot perform a static analysis."

Now, I have a kind of defensive programming style already, with guarding exceptions like this:

if(var == null) { throw new NullArgumentException(); }

I'm also using NullObject Pattern alot and have rarely any problems. Add Unit Tests to it and you're all set up.

I have never used asserts and never missed them. Quite the contrary. I really hate code that has lots of meaningless asserts in it, which is just noise to me and distracts me from what I really want to see. Code contracts, at least the Microsoft way are much the same - and even worse. They add lots of noise and complexity to the code. In 99% an exception will be thrown anyway - so I don't care whether it's from the assert/contract or the actual problem. Just very, very few cases remain in which the program states really becomes corrupted.

So frankly, what is the benefit of using code contracts? Is there any at all? If you already use unit tests and code defensively I feel that introducing contracts is just not worth the cost and puts noise in your code that a maintainer will curse when he's updating that method, much like I do when I cannot see what the code is doing due to useless asserts. I have yet to see a good reason to pay that price.

Falcon
  • 19,248
  • 4
  • 78
  • 93
  • 1
    http://research.microsoft.com/en-us/projects/contracts/faq.aspx – gws2 Sep 12 '13 at 21:56
  • 1
    Why do you say "it does not and often cannot perform a static analysis"? I thought that was one of the points of this project (as opposed to just using Asserts or defensive exception throwing)? – Carson63000 Sep 13 '13 at 04:17
  • @Carson63000 Read the documentation carefully and you'll find that the static checker will just output lots and lots of warnings, most unecessary, (the developers admit it to be so) and that most defined contracts will just throw an exception and do nothing besides. – Falcon Sep 13 '13 at 06:28
  • @Falcon: strangely, my experience is completely different. Static analysis works not flawlessly, but pretty well, and I rarely see unnecessary warnings, even when working at warning level 4 (the highest). – Arseni Mourzenko Sep 13 '13 at 06:48
  • I guess I'll have to try it to find out how it behaves. – Falcon Sep 13 '13 at 08:28
  • Code Contracts can run in two modes: As a static checker or simply as runtime guard clauses. The two modes can also be combined or the compiler can remove all contracts after checking. – John Nilsson Sep 13 '13 at 20:16
  • Static check should be be complete (u less there are bugs) such that if it compiles then the contract is verified to be true. If verification fails you might have to help it by adding more proofs (contracts) to the code, it won't compile until you do. – John Nilsson Sep 13 '13 at 20:19
  • Do note that by using Contract.assume and such you are by passing the contracts which might the verification draw the wrong conclusion. It just like adding type casts, it's now your responsibility to make sure that it is necessary true. – John Nilsson Sep 13 '13 at 20:23

4 Answers4

43

You might just as well have asked when static typing is better than dynamic typing. A debate that has been raging for years with no end in sight. So have a look at questions like Dynamically vs Statically typed languages studies

But the basic argument would be the potential to discover and fix issues at compile time that might otherwise have slipped into production.

Contracts vs. Guards

Even with guards and exceptions you are still faced with the issue that the system will fail to perform it's intended task in some instance. Possibly an instance that will be quite critical and costly to fail at.

Contracts vs. Unit tests

The usual argument on this one is that tests prove the presence of bugs, while types (contracts) prove the absence. F.ex. using a type you know that no path in the program could possibly supply invalid input, while a test could only tell you that the covered path did supply the correct input.

Contracts vs. Null Object pattern

Now this is at least in the same ball park. Languages like Scala and Haskell has had great success with this approach to eliminating null references entirely from programs. (Even if Scala formally allows nulls the convention is to never use them)

If you already employ this pattern to eliminate NREs you've basically removed the largest source of runtime failures there is in basically the manner contracts allow you to do it.

The difference might be that contracts has an option to automatically require all your code to avoid null, and thus force you to use this pattern in more places to pass compilation.

On top of that contracts also give you the flexibility to target things beyond null. So if you no longer see any NRE in your bugs you might want to use contracts to strangle the next most common issue you might have. Off by one? Index out of range?

But...

All that being said. I do agree that the syntactic noise (and even structural noise) contracts add to the code is quite substantial and the impact the analysis has on your buildtime should not be underestimated. So if you decide to add contracts to your system it would probably be wise to do so very carefully with a narrow focus on which class of bugs one tries to address.

John Nilsson
  • 546
  • 5
  • 5
  • 6
    That's a great first answer on Programmers. Glad to have your participation in the community. –  Sep 13 '13 at 00:03
13

I don't know where the assertion that "it does not and often cannot perform a static analysis" comes from. The first part of the assertion is plainly wrong. The second one depends of what you mean by "often". I would rather say that often, it performs static analysis, and rarely, it fails at it. In ordinary business application, rarely becomes much closer to never.

So here it comes, the first benefit:

Benefit 1: static analysis

Ordinary assertions and argument checking have a drawback: they are postponed until the code is executed. On the other hand, code contracts manifest themselves at a much earlier level, either at the coding step or while compiling the application. The earlier you catch an error, the less expensive it is to fix it.

Benefit 2: sort of always up to date documentation

Code contracts also provide a sort of documentation which is always up to date. If the XML comment of the method SetProductPrice(int newPrice) tells that newPrice should be superior or equal to zero, you may hope that the documentation is up to date, but you may also discover that somebody changed the method so that newPrice = 0 throws an ArgumentOutOfRangeException, but never changed the corresponding documentation. Given the correlation between code contracts and the code itself, you don't have the out-of-sync documentation problem.

The sort of documentation provided by code contracts is also precious in a way that often, XML comments don't explain the acceptable values well. How many times was I wondering if null or string.Empty or \r\n is an authorized value for a method, and XML comments were silent on that!

In conclusion, without code contracts, lots of pieces of code are like that:

I'll accept some values but not others, but you'll have to guess or read the documentation, if any. Actually, don't read the documentation: it's outdated. Simply loop through all values and you'll see the ones which make me throw exceptions. You also have to guess the range of values which may be returned, because even if I would tell a bit more to you about them, it may not be true, given the hundreds of changes made to me for the last years.

With code contracts, it becomes:

The title argument can be a non-null string with a length of 0..500. The integer which follows is a positive value, which can be zero only when the string is empty. Finally, I'll return a IDefinition object, never null.

Benefit 3: contracts of interfaces

A third benefit is that code contracts empower interfaces. Let's say you have something like:

public interface ICommittable
{
    public ICollection<AtomicChange> PendingChanges { get; }

    public void CommitChanges();

    ...
}

How would you, using only asserts and exceptions, guarantee that CommitChanges can be called only when PendingChanges is not empty? How would you guarantee that PendingChanges is never null?

Benefit 4: enforce the results of a method

Finally, the fourth benefit is to be able to Contract.Ensure the results. What if, when writing a method which returns an integer, I want to be sure that the value is never inferior or equal to zero? Including five years later, after suffering from lots of changes from lots of developers? As soon as a method has multiple return points, Asserts become a maintenance nightmare for that.


Consider code contracts not only as a mean of correctness of your code, but a stricter way to write code. In a similar way, a person who used exclusively dynamic languages may ask why would you enforce types at language level, while you can do the same thing in assertions when needed. You can, but static typing is easier to use, less error-prone compared to a bunch of assertions, and self-documenting.

The difference between dynamic typing and static typing is extremely close to the difference between ordinary programming and programming by contracts.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
3

I know this is a little late to the question, but there is one other benefit to Code Contracts which bears mentioning

Contracts are checked outside the method

When we have guard conditions inside our method, that is the place where checking happens and where errors get reported. We might then have to investigate the stack trace to find out where the actual source of the error is.

Code contracts are located within the method, but preconditions are checked outside the method when anything attempts to call that method. The contracts become part of the method and determines whether it can be called or not. That means that we get an error reported much closer to the actual source of the problem.

If you have a contract protected method which is called for many places, this can be a real benefit.

Alex White
  • 141
  • 2
2

Two things really:

  1. Tooling support, VS can make contract data apart of intellisence so that it is part of auto complete help.
  2. When a subclass overrides a method you lose all your checks (which should still be valid if you follow the LSP) with contracts they follow their child classes automatically.
stonemetal
  • 3,371
  • 16
  • 17