Good fences make for good neighbors

If you work in an environment as professional as mine, automatic testing is probably a given.

Odds are, also, that the expression “Design by Contract” (or DBC) isn’t. Maybe that you even never heard about it.

So, what is it?

DBC, in short

If you want to know more about “Design by Contract” and “Assertive Programming” in general,  “The Pragmatic Programmer” (Thomas, Hunt) has a section about each to get you started.

Design by contract is a form of assertive programming where you ensure that functions/methods only operate on sound input and produces the output expected.

You can also identify “class or state invariants”: conditions that should hold before and after each function/method execution, and enforce them.

How do you implement it?

Some languages, like Clojure, have built-in support for DBC (I have briefly mentioned this technique in a previous entry on my blog), for example:

(defn function [arg1 arg2]
    {:pre [(is-foo? arg1) (is-bar? arg2)}]
     :post [(looks-right? %) (sparks-joy? %)]
    ;; your code here
)

will throw an exception if the checks on arg1 or arg2 fail, or if the function result will not "look right" or does not "spark joy".

In other languages, you can still make do. The first time I used DBC was with Objective C on an iPad application. Since the language doesn’t support it, I used a mix of assertions and preprocessor macros.

If I had a function and I wanted to make sure it would add a single value to a non-empty array, I would have done something like:

// PSEUDOCODE!
void appendValue(NSMutableArray *arg) {
#ifdef DBC
    NSUInteger origSize = [arg count];
    assert(origSize > 0); 
#endif

// Do your thing

#ifdef DBC
    assert(origSize + 1 == [arg count]);
#endif
} 

It’s clunky – I know – but it works, and proved invaluable thorough development.

Plain Java can get something similar using the assert construct but is limited by the extensive use of mutability (just as in the previous example) and lack of preprocessing.

Why use it?

First of all, if you are thinking now: “Well, I do TDD or UT, I have full coverage: I don’t need DBC!” you would be factually wrong.

DBC is complementary to unit testing, and it provides value whether you have a testing infrastructure in place or not.

DBC tests the application while it runs, where UT – even a comprehensive end-to-end test – will only check a selected scenario.

One of the main strengths of UT (at least design-wise) is that it’s a form of black-box testing: it ensures that the test subject produces the expected outcome in a set scenario, without delving into the object’s state.

DBC, on the other hand, ensures that your application is internally coherent at runtime as it checks the internal state, and it’s an inherently low-cost approach.

Consequently, you can easily practice DBC in situations where UT and TDD have a prohibitively high cost/benefits ratio, like legacy code or poorly designed applications.

Why is DBC not popular?

If DBC is so good, why isn’t it as popular as TDD or UT?

One of the reasons – I think – is that Test-Driven Development and Unit Testing are both safety nets and design techniques.

DBC – on the other hand – helps the developer to think about the scope of each function but does not lead to clean code.

I suspect – however – that this is only part of the reasons.

I think the main reason why we don’t use it is that – as developers – we tend to be complicit with our code.

It’s the same reason we don’t do assertive programming: we are deeply uncomfortable with the idea of making our code crash, even if we are sure it’s not doing the right thing.

We root for our code, beyond reason.

We think:

Wait…

Is my method unexpectedly receiving an empty list?!

Well, let’s still give it a chance!

Go on anyway, "boolean checkNonEmptyList(List arguments)": I believe in you!

Exemplifying the first week of my current job, I was doing pair-programming with a senior team member. The guy is reviewing my code.

At some point, he sees a (rather tame) assertion I had put in one of my methods. He looks at me aghast and mutters:

We don’t put assertions in the code. We don’t do this.

As of now and that I know, assertions are still taboo in my workplace (a workplace which is – to be crystal clear – a place full of very respectable and capable developers).

Checks are still made of code

I’ll play the devil’s advocate at this point.

There is at least a good reason to look at DBC with distrust: the checks you are adding in your code are – of course – still code.

And like any code, it will contain bugs and it will fail on occasion, so the idea of adding a check that may actually add a bug to your code does not win a popularity contest.

Sure, you can be careful all you want but, eventually, your application will crash due to a bogus contract.

Is then DBC still worth it? YES. The number of bugs you’ll catch with DBC fairly exceeds the bugs you will introduce, and finding one of the latter contributes to your understanding of your code.

Yet, this is probably a (more reasonable) explanation of why this technique is less used and why I respectfully disagree with those who would leave all DBC checks on in production by default.

Conclusions

“Design by Contract” should be in every programmer’s toolbox: it forces you to think about the scope of your code, provides runtime testing, and is cheap to implement.

Like everything in this world, it isn’t devoid of downsides, but the benefits far outweigh the risks.

Leave a Reply

Your email address will not be published. Required fields are marked *