Slashdot Mirror


Why Is "Design by Contract" Not More Popular?

Coryoth writes "Design by Contract, writing pre- and post-conditions on functions, seemed like straightforward common sense to me. Such conditions, in the form of executable code, not only provide more exacting API documentation, but also provide a test harness. Having easy to write unit tests, that are automatically integrated into the inheritance hierarchy in OO languages, 'just made sense'. However, despite being available (to varying degrees of completeness) for many languages other than Eiffel, including Java, C++, Perl, Python, Ruby, Ada, and even Haskell and Ocaml, the concept has never gained significant traction, particularly in comparison to unit testing frameworks (which DbC complements nicely), and hype like 'Extreme Programming'. So why did Design by Contract fail to take off?"

15 of 178 comments (clear)

  1. Comment removed by account_deleted · · Score: 4, Insightful

    Comment removed based on user account deletion

  2. no bang for your buck by majiCk · · Score: 3, Insightful

    Design by contract seems like a lot of extra work and runtime cost for something that might once in a while catch a bug in already-deployed code. Lighter weight methods like static typing catch (certain kinds of) errors before the code is even compiled; unit testing is usually done before code is deployed, and with the express aim of exposing incorrect behavior in corner cases.

    Just what niche is design-by-contract supposed to fill? It's heavyweight, costly at runtime, undirected, and likely to catch bugs only after deployment -- too-little-too-late. Maybe it's unpopular because it's a poor tradeoff.

    1. Re:no bang for your buck by barrkel · · Score: 4, Insightful

      In a phrase, the niche is professional coders, rather than hack-a-day cowboys.

      Assertions, whether invariants, preconditions or postconditions, can be viewed as extensions to the type system [1]. Since they are expressions of a boolean type and don't modify state (at least, no sane ones would mutate state), they're very easily analysed.

      Combine that with a runtime like .NET or the JVM. Compilers and analysers can use such contracts to perform extra typechecking of e.g. arguments to methods, where the methods have preconditions. "Runtime cost" doesn't exist if the runtime has support, especially where verification also exists. Consider the old conundrum: do you check your parameters, or do you expect your caller to check the arguments before they're passed? What if the parameters being wrong leads to a subtle error that manifests later rather than a dramatic failure? With properly stated contracts, these checks can be inserted at the lowest level and automatically propagated higher statically by the compiler or dynamically by the runtime as necessary.

      The niche that DbC is supposed to fill is formalising what is already recognised good practice - stating your assumptions while writing your code. If you have much experience as a programmer, you'll know that it's useful to use assert, ASSERT, or some other feature of your environment at choice places where you make certain assumptions that you think can't be broken. And if you've read The Pragmatic Programmer, you'll know why you'll want to leave those assertions in the final release.

      Most popular languages, such as C#, Java, C++ etc., capture very little programmer intent in their type systems. There's a lot more potential there - preconditions and postconditions can e.g. state that a variable will be in a certain range dependent on the values of other variables, and then automatically detect range mismatches with e.g. loop invariants. Basically, assertions are really useful extensions to the type system.

      [1] For example, consider that a 'requires x != null' constraint and a notional non-nullable reference type from C# or Java, analogous to 'MyType!' in C-w (C Omega):
      http://blogs.msdn.com/cyrusn/archive/2004/06/03/14 7778.aspx

    2. Re:no bang for your buck by Coryoth · · Score: 3, Insightful

      Design by contract seems like a lot of extra work and runtime cost for something that might once in a while catch a bug in already-deployed code. Lighter weight methods like static typing catch (certain kinds of) errors before the code is even compiled; unit testing is usually done before code is deployed, and with the express aim of exposing incorrect behavior in corner cases. DbC doesn't incur any runtime cost if you choose not to bother - any good contract system allows you to turn off contract checking for production code. That is, DbC provides a test harness during testing but needn't do any checking of finished code after testing is complete. Moreover good contract systems like JML and Spec# are smart enough to allow static checking which allow you to catch a whole host of errors before compilation that static types just aren't going to be able to catch. And I have to admit that I am unsure how DbC is "a lot of work" given that you should be writing unit tests anyway, and if you have constraints or invariants then you ought to be checking those with unit tests - writing the constraint as a contract is less work than writing the equivalent unit test. Finally DbC enables testing that for which unit tests of corner cases simply can't compare - by using the contracts as a test oracle you can do automated randomised testing of several integrated units. That is to say, you can automatically generate data to search the input space delimited by preconditions, and use postconditions, invariants, and preconditions of any called code, to test that several units all work together correctly and potentially catch weird interelated corner cases that you hadn't anticipated. Honestly, check out AutoTest or Quickcheck sometime, they are suprisingly powerful and have found bugs in established production code that had already undergone extensive standard testing.
  3. Extreme Programming by andy314159pi · · Score: 4, Funny

    I employ what is known as "Extreme Programming." It mostly involves alot of screaming at the monitor and yelling at my coworkers. I get fired alot but I never have a hard time getting a new job with my Extreme Programming skills that I write all over my resume.

  4. It needs serious language support by Animats · · Score: 5, Informative

    If you're serious about design by contract, you need to use a language that supports it. Eiffel does, of course, and so does "Spec#", Microsoft's verifiable variant of C#, but other than that, "support" is a collection of half-baked add-ons that don't provide any strong assurances.

    If you're going to take object invariants seriously, you have to take object invariance seriously. Objects can't be allowed to change other than when control is inside them, and when control is inside the object, no public method of the object can be called. This means you have to be able to catch cases where object A calls object B which then calls a public method of A. The invariant of A isn't established at that point, and so, calls into A are illegal. This strict notion of inside/outside is fundamental to class invariants, but many so-called "design by contract" approaches gloss over it. You need a way to explicitly say "control is now leaving this object temporarily" when calling out of an object, and the object's invariant must be true at that exit.

    Threading and locking have to be handled in the language. The language needs to know which locks protect what data, or invariants aren't meaningful.

    Then there's the problem of how to express an invariant, entry, or exit condition. Are quantifiers provided, or what? How do you talk about concepts like "forward and back pointers of the tree must be consistent"? There's known formalism for that sort of thing, but it's not something you can express cleanly in, say, C or C++.

    Without smarts in the compiler, run time checking tends to be too expensive. The compiler needs to know that member function F can't change member variables X and Y, and therefore, invariants concerning X and Y don't have to be rechecked. Without optimizations like that, you end up rechecking everything on every call to every access function.

    I'd like to see more design by contract, and I'd like to see it work well enough that when something breaks, you know which side of the interface to blame. I used to do proof of correctness work, and it's quite possible to do this. But you can't do it in C or C++; the languages are too loose. It's been done well for Modula and Java, and a DEC R&D group had a very nice system going just before Compaq canned DEC's Palo Alto research labs. The rise of C killed off verification work; the decline of C may bring it back.

  5. Re:Management by ShieldW0lf · · Score: 3, Insightful

    A good business analyst will answer the question "What is it that we (the client) need." They help make sure that the excellent code you write, when it's doing as you were hired to write it to do, solves problems instead of making them.

    A good architect will help establish clear separation of authority, giving team members more autonomy to go do what they're good at without having other peoples fingers in their pies or needing to leave their area of scope.

    There are a lot of people with pieces of paper from a school that are terrible at these things, and they muddle along leaving wreckage behind them. But that doesn't dismiss the value of having someone competent in those roles when you can find them.

    --
    -1 Uncomfortable Truth
  6. Re:Who cares? by Just+Some+Guy · · Score: 4, Insightful

    Who cares if it's popular? If it solves your problem, use it.

    Two reasons: first, the more people use it, the wider it will be supported. It's nice to be involved with something on the upswing that more people are interested in than bored with. Second, it's nice to know why something's unpopular. Maybe it's hard to use, but you're a genius and will be able to put it to work. Or, maybe, everyone else realized that it's awful and moved on to something less heinous.

    --
    Dewey, what part of this looks like authorities should be involved?
  7. Too many layers by Geoffreyerffoeg · · Score: 3, Insightful

    You know the aphorism about how any CS problem can be solved by another layer of indirection -- except the problem of too many layers of indirection. That's what design-by-contract is. Instead of having the intrinsic type-safety checks and the social trust that the code author has run unit tests if necessary and makes the code do something reasonable, design-by-contract formalizes all this and makes you specify conditions on the code manually. That's quite a bit of effort for relatively little advantage. The popular design-by-informal-agreement works almost as well and doesn't have the extra, unneeded layer of indirection.

  8. Already mostly done by Todd+Knarr · · Score: 3, Informative

    As has been noted, most programmers already do design-by-contract, they just don't call it that. They call it argument checking. The first thing most routines do is validate their arguments, and return an error if any of them are invalid. The last thing done is to check the results and return an error if the results aren't valid. The calling code then checks for error codes or invalid results (eg. a search function returning a null pointer indicating the item wasn't found).

    In the real world I often skip this overhead when the conditions are enforced elsewhere. For example, a data structure needed by an internal function may not have to be checked for existence since if it hadn't been created my initialization function would've detected this and signaled an error and the program would've exited. In cases like that, I either omit the check or wrap it in an ifdef so it's only done during development and ignored by the compiler during the release build.

    Don't make the mistake of confusing the name of a concept with the concept itself. You'll find quite often that that nifty shiny-new concept someone's presenting as their own has actually been around for 30-40 years and they've just added some chrome, filed off the serial numbers and changed the name to keep you from noticing this.

  9. been doing that for years already by imbaczek · · Score: 5, Insightful

    assert(condition) is your friend. It's not called a contract, it's not design (but a very good practice!) and it does the job well.

  10. Re:I'd like to use DbC, but... by Coryoth · · Score: 4, Informative

    I don't want my software to fail in the field (at my day job, we write stock trading software - reliability is key because lack of availability can quickly become very expensive). If I could define a number of pre- and post-conditions for each function and have the compiler check these for me, I'd be happy. And indeed, this can be done, and is available for a number of DbC systems. Check out JML which has ESC/Java2 to provide static contract checking for Java, Spec# (C# with contracts) which uses the Spec# verifier for static checking of contracts, and Eiffel with ESpec-Verify for static checking of Eiffel contracts.

    If the conditions are only going to be checked at runtime, then I'm going to have to write unit tests anyway - otherwise, the failure's going to be beautifully detected and localised and so forth, but crucially, it's going to be one of my customers that detects it. If I'm writing unit tests anyway, why bother with DbC? The difference between DbC and unit tests (and really, you should be doing both) is that if a test can be expressed as a constraint then it is useful to simply express that as a contract, while if the test is a specific input to output matching test then it is going to be useful as a separate unit test. When you run your unit tests the contract constraints will automatically get checked. More importantly they will help isolate exactly where the error occured when testing integrated systems. Furthermore, by putting constraints as contracts you have improved your API documentation (any decent DbC system includes automated inclusion of contract information in API documentation) which helps other people use your code correctly, and makes maintenance easier.

    Finally contracts allow automated testing. That's where you automatically generate data to pass to the code and let the contracts act as a test oracle to catch and locate problems. With something like AutoTest for Eiffel the data generation can be purely random (constrained by preconditions of course), or designed to sample the input according to best coverage via genertic algorithms, etc. The result is that you find corner cases that you might not have anticipated with your unit tests - and you would be surprised how often that happens, AutoTest found a number of subtle bugs in Eiffel's base libraries which had been production code for years.

    When there's a DbC language or add-on that checks the contracts at compile time, I'll be interested. Then you really need to check out JML and ESC/Java2, and Spec#, because you would be interested.
  11. Re:Management by Anonymous+Brave+Guy · · Score: 4, Interesting

    People have a tendency to confuse bluster and arrogance with ability and experience.

    They do, yet in my experience, these things are inversely correlated. This applies to all guidance/oversight roles, including business analysts, software architects, consultants, and indeed managers themselves. As I've commented here before, you can always identify a good leader by three characteristics:

    • They can set a clear direction and convey this to others.
    • They organise adequate resources and set realistic expectations.
    • Having done the above, they get out of the way as much as possible.

    The third one is usually the easiest way to identify morons. If you come across a leader in software development who places more value on reports and metrics so they can track things than they do on supporting the developers and test teams working for them, then you know you're dealing with an incompetent.

    And yes, that does mean many leaders in software development organisations today are incompetent. That's why the genuinely good people are worth so much.

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
  12. Or, better yet... by Chemisor · · Score: 4, Informative

    > assert(condition) is your friend.

    And assert(condition && "Explanation of why it's bad and what to do to fix it") is even better. Don't make me read your code and figure out why the hell you put some obscure assert(n != 455) in there.

  13. Re:Design by contract is 25+ years old by Waffle+Iron · · Score: 3, Funny

    We call it include files and C coders have been doing it for ages. You noobs and your silly OO languages.

    I especially like contractual gems such as:

    char *gets(char *s);