By way of a quick summary design-by-contract (DbC) is a software design technique that dates from the '50s. The notion was formalized initially for OO development by Bertrand Meyer in the early '80s and it has been steadily refined since then. The basic idea is that that there is a contract between client and service. The client specifies something to be done and the service is designed to satisfy that specification.
This sort of client/service relationship is a rather procedural view that had been modified in subtle ways in the OO context. In OO development the notion of contract has been abstracted and formalized around the notion of interface. Thus a client is not a specific entity in a particular problem context but a more generic notion that is closer to the idea of a bundle of requirements and a message. The bundle of requirements represents the specification for the responsibilities the object must fulfill to solve the problem in hand and the message defines an interface for accessing those particular responsibilities. So the notion of client has been generalized to any other object that might require access to the responsibilities.
So in an OO context DbC is executed by: (1) formally specifying the responsibilities an object must contribute to the solution of the problem; (2) define a generic interface to access those responsibilities that any object can access; and (3) design the implementation of the service to support the specification and the interface. One common technique to do (1) is to define preconditions and postconditions that must prevail before and after executing the responsibility as well as invariants that must prevail during the service execution.
The preconditions define obligations that the client must satisfy. The postconditions define the expectations of the client that the service must satisfy. The invariant conditions define constraints on the execution itself.
Since object state machines separate message and method and a state is defined as a condition where particular rules and policies apply, we have an ideal situation to apply DbC. Since the state is a condition that must exist for the execution of the action's rules and policies, it is effectively a precondition for executing those rules and policies. However, that condition can only exist as a postcondition of some other state action executing (i.e., some action must execute to modify the application condition).
Therefore we can define the sequence of execution by essentially "walking" through the conditions leading to the solution backwards. For an given state action to execute, its condition must prevail so one looks for another action whose postcondition is the same. One then generates the event to trigger the transition in that action. In this scenario the events are messages that simply announce that a postcondition prevails. The developer just matches that postcondition to another object's condition and consumes the event there.
Thus one has a daisy chain of precondition-to-postcondition contracts that define the actual sequence of messages for the problem solution in a mechanical fashion. A corollary is that one can specify all the object state machines prior to worrying about the flow of control of the overall problem solution. This deferral of concerns about sequences of operations is probably the most fundamental difference between OO development and procedural or functional development.
Do OO developers actually do this sort of thing in practice? Usually not literally unless especially thorny sequencing problems arise. That's because an experienced developer can multiplex and knows how responsibilities were allocated in other state machines when implementing the one in hand. However, conceptually they do because they separate message and method, treat messages as announcements (I'm Done) rather than imperatives (Do This), assume peer-to-peer collaboration on an as-needed basis, and construct object state machines around intrinsic life cycles of problem space entities.
A caveat. Life is rarely quite so simple. In OO development one also captures the notion of state in state variables (knowledge attributes) and well as state machine states. That is an orthogonal process with its own sequencing problems because of data consistency issues. So the contract precondition is likely to be compound. One condition will always be the state condition but it may be ANDed with conditions on various state variables being updated correctly.
Any update of a state variable must be done be some behavior responsibility so it will always be in an object state machine action if one employs object state machines to describe all behaviors. That means we can replace the data update precondition with the postcondition of the action that does the update. As a practical matter we do that by daisy chaining the conditions. So if we have dependencies that look like:
[PostconditionB] ------------ [DataX] / / / [PreconditionA] \ \ \ [PostconditionC]
then rather than having both PostconditionB and PostconditionC sending events to PreconditionA for synchronization, we can reorganize the dependencies as:
[PostconditionB] ------------ [DataX] | | | [PreconditionA] | \ | \ | \ | [PostconditionC]
Now part of the compound precondition of PostconditionC is that PostconditionB executed. So the action with PostconditionB sends an event to the object with PostconditionC and then that object sends an event to the object with PostconditionA when responding with PostconditionC.
The astute observer will note that I glossed over the fact that some action not shown triggered PostconditionC, so I have just moved the problem. Again, it is beyond this scope to go into details but I submit that most state actions modify state variables so their postconditions are effectively announcing that update. I also submit that since actions are logically indivisible, they should not encapsulate complex behaviors relative to the problem in hand. So as a practical matter it is usually pretty easy to organize the proper sequence.
I only went into this digression to make the point that matching action conditions to action postconditions is a rigorous and systematic approach to ensuring the correct flow of control to solve the problem. However, that is only possible if all behavior responsibilities are described with object state machines. If one mixes in synchronous behaviors that update state variables, there is no rigorous and systematic way to ensure correct flow of control -- it is a pure developer responsibility. IOW, this is a strong reason for describing all behavior responsibilities with state machines.