#begin

 

Design by Contract

David and Thomas say that on of the best solutions for ensuring plain dealings is by contract. We have probably signed lots of contracts before like an employment contract, a mortgage or maybe a lease for a car. These contracts specify the rules and agreements for both parties and are an official document recognized by certain agencies or governments for example. They are essential for how modern society functions.

We can do the same for software. A guy called Bertrand Meyer developed the concept of design by contract for the Eiffel programming language. It’s simple yet powerful and focuses on documenting rules and responsibilities to ensure program correctness. David and Thomas say that program correctness means that a program does do no more and no less than it claims. Documenting and verifying that claim is what Design By Contract (DBC) really means. Mr. Meyer described three aspects of DBC.

  • Preconditions
  • Postconditions
  • Class Invariants.

We talked about the concept of pre and post conditions before. But not about Class Invariants, so what are those? This means that some condition is always true from the perspective of the caller. This condition is true during precondition checks, and postcondition checks. The book does mention the fact that, during a function’s operation the invariant might not hold. But as soon as the function needs to return data, the invariant must be true again.

Next they show an example DBC in a Java context implemented with a package called iContract. I’ve never heard of this in the wild and totally forgot about this. In the code snippet they show some list class being annotated with additional preprocessor data to impose these contracts. I’m not a big fan of this. I don’t really like these kind of annotations in code since they add coupling you wouldn’t otherwise have. But what’s you opinion about this? Do you like annotating classes, even domain objects with such specialized data? I don’t… so what about you? I like the way Clojure handles this far more. It’s actual code at the start and end of your function.

They do add some additional comments about the pre and-post conditions and invariants and I’ll quote it: “If all the routine’s preconditions are met by the caller, the routine shall guarantee that all postconditions and invariants will be true when it completes.”

This is a very powerful concept. Imagine that, if you can rely on the fact that if you pass in the correct input to a function, the function will always complete successfully. This sounds awesome right? I think this why people are such fans of functional programming. In functional programming, such functions are often the way a system is designed. You probably have heard about pure functions, or functions without state or side-effects. I’m not saying a DBC function cannot have state or side-effects but it does make your life a whole lot easier.

But to get back to the book. David and Thomas refer to DBC code as being “lazy” code. They say to be strict about what you will accept before you begin and promise as little as possible in return. If your function accepts everything, and promises anything. That’s one hard function to write and maintain. So be strict and make your life easier.

One way of designing by contract is be using a very well known principle. It’s L of the SOLID principles, the Liskov Substitution Principles. Remember what the L stands for? It stands for the concept that you should be able to treat an implementation exactly the same way as it’s specification. Or maybe more specific, you can treat a base class exactly the same way at it’s parent, without you even knowing you are actually dealing with an inherited class of an interface for example.

Interfaces and abstract classes can be the contract that you design to. In a Unity3D context we can treat any let’s say Selectable in the UI the same way. We can operate on Selectables, we don’t care if they are actually buttons, textboxes, inputs or dropdowns. We just care about selecting it. This is the power of inheritance, polymorphism and of course the DBC aspect.

So implementing an interface and adhering to its contract helps you design proper systems. If you don’t state contracts explicitly you are programming by coincidence. Haha, you have probably heard that term before as well I suppose. Programming by coincidence also refers to programming without writing any tests. It’s by mere coincidence that the code works…

 

Assertions

If you do not have access to a specialized framework or libraries for imposing DBC then inheritance is your way to go. You can however have the compiler help you with checking pre-and-post conditions by adding assertion statements in your production code. They refer to this as Assertive Programming. David and Thomas have an entire section dedicated to this practice but hint at it now. They say that assertions can help you defining pre and post conditions. It doesn’t cover the entire spectrum of DBC but it will get you somewhere. They say that assertions cannot propogate the inheritance tree. And in OO code that can pose real problems. So you end up repeating yourself over and over again. I’m not entirely sure how Clojure implements this but it has of course the benefit of not being an OO language. But the problem boils down to the contract not being automatically enforced, which is something you really want.

Next they mention language support. This is rather limited. They refer to Eiffel of course but that’s about it. There are libraries you can add to c++ or Java code but those do not add support at the language level. Preprocessors aren’t as good as built-in facilities, that’s a fact. These libraries can still be helpful and will certainly prove to you magic still exists in this world if you have never used a DBC strategy.

Then they mention something really interesting which I’ll quote: “DBS fits nicely with out concept of crashing early.” How true is that statement. We see it almost everywhere now. Especially in the Micro-service world, a fail fast mentality is preferred because failures don’t propagate through a large distributed system. If a service fails, let it fail and don’t make 10 other services fail as a consequence. If you would let things running while you already knew they failed, it will create far more problems down the line.

This also goes in game-development. If you know things failed, don’t keep they running at 60 fps because it will drain performance… Just fail early and recalibrate. This will allow you to create far more robust systems.

It will also help you diagnose problems more easily because you are at the site of the problem, not 10 stack frames up. Or even worse when doing coroutines or thread dispatching you cannot even trace back the origin of the problem!

 

Other uses of Invariants

Then David and Thomas mention the concept of invariants again. These are the kind of conditions that must hold true during the execution of your function. One kind of invariant is a loop invariant. Oh boy, I remember this from a course in Uni where we had to mathematically proof the correctness of a function and you had to write these invariants on paper during an exam. I found that pretty difficult to get right.

But in DBC framework or libraries you can write these invariants to validate that the invariant is true before, for example a loop runs, and it should remain true while the loop is running. These can also be written as assertions but they are again less expressive and require more work I guess. I’m not sure, I’ve never written such conditions for my code.

Next they mention semantic invariants. These are the kind of invariants that should uphold some kind of policy. The example David and Thomas give in the book is for a system they wrote for debit card transactions. They had encoded a semantic invariant that stated that the code should throw an exception instead of executing a duplicate transaction. This basically means the system would not allow 1 transaction to be executed multiple times because it will get out of sync and of course, you loose money. This is thus more on the lines of requirements than actual error checking, hence the name semantic invariant.

And lastly they talk about dynamic contracts and agents. Until know we have only talked about immutable specifications as pre and post conditions and these invariants. But sometimes you require a more dynamic approach and agents are here to help. These agents allow you to make certain choices at runtime. Agents can even be instrumented with AI to make decisions in favor of the consumer. In an ideal world, you could have the agents deciding everything in your system, but we know that doesn’t hold up haha.

So… that’s it for the first subject of chapter 4. We talked in depth about Design by Contract. But how is this important to Unity3D game-development, especially considering C# doesn’t have language support for DBC constructs and I don’t see anyone applying any custom frameworks or libraries to instrument C# to do so.

I think the benefits are subtle. We should implement proper sanity checks and validate in and outgoing data at architectural boundaries or bounded contexts. This practice is always good no matter the language or system you are writing. I believe the key takeaway from this section is to apply some thought, some thinking, about the functions you write. Anticipate the inputs and outputs to create more robust systems.

 

#end

01010010 01110101 01100010 01100101 01101110

Hey, sorry to bother you but you can subscribe to my blog here.

Never miss a blog post!

You have Successfully Subscribed!