Design by Contract Part 1:
The developer's safety net

This is the first article in a series about Design by Contract.

Part 1 : The developer's safety net

Part 2 : Pragmatic DbC in Groovy

Part 3 : Software specification



Design by Contract ("DbC" for short) was invented by Betrand Meyer and first appeared as part of the Eiffel programming language. Eiffel's implementation of DbC is very complete, arguably complex and requires the advanced language features of Eiffel (which common languages don't have) to make it work well. So in this series I won't give a complete explanation on DbC. If you're interested in Eiffel's complete implementation, you can watch the following videos  from Eiffel Software Inc. :



In this article I'll present "the poor Man's" DbC which is more paractical in that it can easily be implemented in common languages without hassle.


DbC in a nutshell

DbC in software engineering draws its analogy from contracts in real life client-supplier relationships. For example a plumber who is contracted to fix a leaking pipe is the supplier of a plumbing service and the owner of the house with the leaking pipe is the client. In order for the job to be completed satiscactorilt there is consideration (to use a legal term) on both parties:


Client consideration (House owner)


      • Give the address of the site

      • Pay for the service


Supplier consideration (Plumber)


      • Provide his own equipment

      • Fix the leaking pipe according to industry standards


A similar list can be specified for software components. For example a "Stock On Hand" object may provide a service to allocate available stock to an order. We would like to tell this object to allocate some stock and update the stock counts given only an order and a product. Here are equivalent considerations in this client-suppler relationship:


Client consideration (Any method of any object)


      • Give a reference to the order 

      • Give a reference to the product to allocate

      • Check that there actually is stock available of that product 


Supplier consideration ("allocate()" method of the "Stock On Hand" object)


      • Create an allocation relationship between the order and the product if it was not already there

      • Decrease the stock on hand count for that product by one

      • Increase the allocation count on the order for that product by one

These considerations translate very well into formally specified conditions in a programming language. Here is one pseudocode example based on Groovy and the DbC framework I developed for the "GSpice" platform:


class StockOnHand


void

allocate(Order order,  Product product) {

    require "An order", order

    require "A product", product

    require "There is stock available",  stockOnHand(product) > 0


    old_count = count(product)

    old_allocation = order.allocated(product)


    // Implementation


    ensure "Product is allocated to order",  order.isAllocated(product)

    ensure "SOH decreased by one", count(product) == old_count - 1

    ensure "Allocation increased by one", order.allocated(product) == old_allocation + 1

}


Integer

count(Product product) {

    // Implementation

}


end class


You'll notice that the client considerations become PRECONDITIONS using the "require" keyword and suppler considerations become POSTCONDITIONS using the "ensure" keyword.

Preconditions are always placed at the top of the method. This makes sense in a real client-suppler relationship too. The house owner (client) must supply the required information (address) before the plumber (supplier) can do her job. It is a requirement of the contract - hence the use of "required" as the precondition.

Post conditions are always placed at the bottom of the method.  This also makes sense in a real client-suppler relationship. The plumber (supplier) must ensure she does her job (fixes the pipe properly) in order to earn the payment. It is a term of the contract that must be ensured before the contract is considered complete - hence the use of the "ensure" keyword.


Notice also that together the pre and post conditions form an almost complete specification for the method. We have not written and implementation code yet but we can have a high degree of confidence that if our implementation passes both all these conditions then it is correct.


The blame game

Finally, the most intersting property of DbC is the dichotomy between require and ensure clauses. It means much more than simply pre and post.  When a condition fails who is responsible to fix the error? Is it the developer who wrote the impelemtation? Or is it the developer who called the service method from some other class?


The answers are simple: 


      • Require failure ==> Blame the method caller (client)

      • Ensure failure ==> Blame the method implementor (supplier)


When DbC conditions are integrated into an exception/logging/alerting system, as I did with GSpice, it becomes trivial to alert the right person immediately and keep them working on the problems within their own responsibility instead of wasting time diagnosing problems that often end up being another person's responsibility. This saves a lot of time during development and testing.


In addition, DbC can save time during production if the the conditions are kept active in the code. The Eiffel camp will say its safe to remove all conditions prior to production, however, in practice most projects don't dare to do this. A reasonable compromise is to leave only the requirements in place as they account for most of the failures encountered after development and testing.

In the Sydney's POS product that used GSpice we left most of the DbC conditions in place, including both requirements and ensures. Due to lack of funding and resources, testing was not comprehensive so in some releases, unknown  flaws were released into production. These were all subtle scenarios that the testers did not have time to cover.

The good news is that we never had a data corruption in the database. There were quite a few bugs that had the potential to corrupt the database and were released into production, however, all of them were caught by the DbC conditions which allowed the transaction system to roll-back the changes to the database and send an automatic e-mail alert about the problem. In this way we prevented serious problems in production, were notified immediately and given accurate information as the exactly where in the code it happened, why (test of the condition) and who was responsible.


I can't imagine a better system for software quality and security. That's what Design by Contract means to me.


In the next article, I'll delve deeper into the DbC framework I implemented for GSpice and cover more advanced features such as requirements for optional method arguments, type checking, diagnostics, conditions in the middle of the implementation code and invariants.



© 2009 Keith Foster. "The Daring Developer" was created for people who share an insane excitement about advanced software engineering.