[Python-ideas] Why is design-by-contracts not widely adopted?

Paul Moore p.f.moore at gmail.com
Fri Sep 28 04:37:05 EDT 2018

On Fri, 28 Sep 2018 at 02:24, Marko Ristin-Kaufmann <marko.ristin at gmail.com>

> Hi,
> I annotated pathlib with contracts:
> https://github.com/mristin/icontract-pathlib-poc. I zipped the HTML docs
> into
> https://github.com/mristin/icontract-pathlib-poc/blob/master/contracts-pathlib-poc.zip,
> you can just unpack and view the index.html.

Thanks, for doing this! It's probably not going to result in the reaction
you hoped for (see below) but I appreciate you taking the time to do it.

Some of the contracts might seem trivial -- but mind that you, as a writer,
> want to tell the user what s/he is expected to fulfill before calling the
> function. For example, if you say:
> rmdir()
> Remove this directory. The directory must be empty.
> Requires:
>    - not list(self.iterdir()) (??? There must be a way to check this more
>    optimally)
>    - self.is_dir()
> self.is_dir() contract might seem trivial -- but it's actually not. You
> actually want to convey the message: dear user, if you are iterating
> through a list of paths, use this function to decide if you should call
> rmdir() or unlink(). Analogously with the first contract: dear user, please
> check if the directory is empty before calling rmdir() and this is what you
> need to call to actually check that.

The problem (IMO) with both of these is precisely that they are written as
Python expressions. Your prose descriptions of what they mean are fine, and
*those* are what I would hope to see in documentation. This is far more
obvious in later examples, where the code needed to check certain
conditions is extremely unclear unless you spend time trying to interpret

> I also finally assembled the puzzle. Most of you folks are all older and
> were exposed to DbC in the 80ies championed by DbC zealots who advertised
> it as *the *tool for software development. You were repulsed by their
> fanaticism -- the zealots also pushed for all the contracts to be defined,
> and none less. Either you have 100% DbC or no sane software development at
> all.

Well, yes, but your claims were *precisely* the same as those I saw back
then. "All projects on PyPI would benefit", "the benefits are obvious", ...

As I said, DbC is a reasonable methodology, but the reason it's not more
widely used is (in the first instance) because it has a *marketing*
problem. Solving that with more of the same style of marketing won't help.
Once you get beyond the marketing, there are *still* questions (see above
and below), but if you can't even get people past the first step, you've

> And that's why I said that the libraries on pypi meant to be used by
> multiple people and which already have type annotations would obviously
> benefit from contracts -- while you were imagining that all of these
> libraries need to be DbC'ed 100%, I was imagining something much more
> humble. Thus the misunderstanding.

No, I was imagining that some libraries were small, some were used by
small, specialised groups, and some were immensely successful without DbC.
So claiming that they would "obviously" benefit is a very strong claim.

> After annotating pathlib, I find that it actually needs contracts more
> thain if it had type annotations. For example:
> stat()
> Return the result of the stat() system call on this path, like os.stat()
> does.
> Ensures:
>    - result is not None ⇒ self.exists()
>    - result is not None ⇒ os.stat(str(self)).__dict__ == result.__dict__
>    (??? This is probably not what it was meant with ‘like os.stat() does’?)
> But what does it mean "like os.stat() does"? I wrote equality over
> __dict__'s in the contract. That was my idea of what the implementer was
> trying to tell me. But is that the operator that should be applied? Sure,
> the contract merits a description. But without it, how am I supposed to
> know what "like" means?
> Similarly with rmdir() -- "the directory must be empty" -- but how exactly
> am I supposed to check that?

Isn't that the whole point? The prose statement "the directory must be
empty" is clear. But the exact code check isn't - and may be best handled
by a series of unit tests, rather than a precondition.

Anyhow, please have a look at the contracts and let me know what you think.
> Please consider it an illustration. Try to question whether the contracts I
> wrote are so obvious to everybody even if they are obvious to you and keep
> in mind that the user does not look into the implementation. And please try
> to abstract away the aesthetics: neither icontract library that I wrote nor
> the sphinx extension are of sufficient quality. We use them for our company
> code base, but they still need a lot of polishing. So please try to focus
> only on the content. We are still talking about contracts in general, not
> about the concrete contract implementation

The thing that you didn't discuss in the above was the effect on the source
code. Looking at your modified sources, I found it *significantly* harder
to read your annotated version than the original. Basically every function
and class was cluttered with irrelevant[1] conditions, which obscured the
logic and the basic meaning of the code.

[1] Irrelevant in terms of the flow of the code - I appreciate that there's
value in checking preconditions in the broader sense. It's like all error
handling and similar - there's a balance to be had between "normal
behaviour" and handling of exceptional cases. And I feel that the contracts
tip that balance too far towards making exceptional cases the focus.

So ultimately this example has probably persuaded me that I *don't* want to
add contract checking, except in very specific cases where the benefits
outweigh the disadvantages. It's very subjective, though, so I'm fine if
other people feel differently.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180928/3da1df6e/attachment-0001.html>

More information about the Python-ideas mailing list