Answering a question by
Wannabeboy in Joel on Software:
The margin of this forum is too small to provide you with a complete theatment of DbC. Bertrand Meyer wrote Object-Oriented Software Construction II (perhaps now even III?) and needed 1252 pages. However, this book is not a bigger-is-better/ more impressive. OOSC II is a classic in software engineering. The Wikipedia and the Wiki Wiki also have useful information.
I will give you a small example of a routine using DbC, written in our own language Carmen (R):
function partMiddle(tt: Text, pos: Card32, len: Nat32)
return Text
-- Returns an extract from
tt, starting at
pos, >>
-- having a length of
len.
-- Aka: midStr (deprecated).
precondition
1 <= pos <= tt.count+1
0 <= len <= tt.count
0 <= pos+len-1 <= tt.count
do
return tt[pos...pos+len-1] -- implementation
postcondition
contains(tt, result)
;
Discussion: contracts are inherited by subtypes; contracts are both executable code and documentation, in fact executable documentation!; contracts can be left out with a compiler switch for better performance (usually not needed, except in hotspots); making sure that preconditions are met is the job of the caller-programmer; making sure that postconditions are met is job of server-programmer; perfect blame management within a team of programmers; when a postcondition is violated there is no point in using a stack trace: the server-programmer has either made a mistake in his implementation code, or had too weak a precondition; code is made as brittle as possible, no attempt to repair mistakes or second guess the caller.
The latter point is important. Make the software as brittle as possible so that bugs are discovered early instead of covered up by usually buggy and badly test-covered exception handling code. Let the code explode in your face, it makes you careful (-8. I estimate that we have fewer than 0.2 exception handlers per KLOC. This style of DbC reduces the number of execution paths enormously.
Not shown in this simple example are intermediate inspections (checks). These are used to assert a condition. These inspections should be used instead of debuggers. If we have a bug in our code (signalled by a violated contract or inspection) we do not fire the debugger; we add more inspections in the code until we find the point where the mistake was made.
Notice that in this example we did no attempt to repair incorrect inputs, such as the caller demanding parts longer than the original input tt. Such repair mechanisms complicate the software tremendously, are never enough to cope with all stupidities people do (thus demanding for even more complexity both in the service and in its existing callers), make it very complicated for anyone to know what the service exactly does, make documentation a nightmare and do not signal the bugs in the caller.
The Carmen-programming language is a blend of Ada and Eiffel, designed for large scale high-integrity applications. Although its syntactic design has resemblance to Python, it actually predates Python by several years. Carmen has many new facilities that make it simple to write correct software. Small programs in Carmen are usually compacter than their Python counterparts, very large programs are much compacter than their Eiffel and Ada equivalents (without leaving out things that make Eiffel and Ada so interesting; I am not making a comparison like that of structured Ada with unstructured Perl) and without giving in on readability.
There is no complete compiler for Carmen yet, because a compiler for Carmen cannot be generated with standard compiler-compilers, so we are writing things ourselves. The language, the libraries, environment and the development method have been developed in pair. At this moment we have a bunch of tools that together allow us to think and write in Carmen.
Now that the birth of a first version of a Carmen-compiler is nearby, I plan to write more on Carmen and language design in my blog.