What should you do when clients fail to give you valid input? They have broken the contract, and you need to let them know. As we mentioned earlier, this is not the best time to abort the program (although you’re justified in doing so since the contract was violated), but an exception is certainly in order. This is why the Standard C++ library throws exceptions derived from logic_error, such as out_of_range.[16] If there are functions that only you call, however, such as private functions in a class of your own design, the assert( ) macro is appropriate, since you have total control over the situation and you certainly want to debug your code before shipping.
Since post-conditions are totally your responsibility, you might think assertions also apply, and you would be partially right. It is appropriate to use an assertion for
assert(0 <= nextSlot && nextSlot <= capacity);
or, if nextSlot is an unsigned integer, simply
assert(nextSlot <= capacity);
Such an invariant is called a
Validating results returned to the client, however, is nothing more or less than
A simple unit test framework
Writing software is all about meeting requirements.[18] It doesn’t take much experience, however, to figure out that coming up with requirements in the first place is no easy task, and, more important, requirements are not static. It’s not unheard of to discover at a weekly project meeting that what you just spent the week doing is not exactly what the users really want.
Frustrating? Yes. Reasonable? Also, yes! It is unreasonable to expect mere humans to be able to articulate software requirements in detail without sampling an evolving, working system. It's much better to specify a little, design a little, code a little, test a little. Then, after evaluating the outcome, do it all over again. The ability to develop from soup to nuts in such an iterative fashion is one of the great advances of this object-oriented era in software history. It requires nimble programmers who can craft resilient code. Change is hard.
Ironically, another impetus for change comes from you, the programmer. The craftsperson in you likely has the habit of continually improving the physical design of working code. What maintenance programmer hasn’t had occasion to curse the aging, flagship company product as a convoluted patchwork of spaghetti, wholly resistant to modification? Management’s knee-jerk reluctance to let you tamper with a functioning system, while not totally unfounded, robs code of the resilience it needs to endure. "If it’s not broken, don’t fix it" eventually gives way to, "We can’t fix it—rewrite it." Change is necessary.
Fortunately, our industry has finally gotten used to the discipline of
Whether the force for change comes from users or programmers, however, there is still the risk that changes today will break what worked yesterday. What is needed is a way to build code that withstands the winds of change and actually improves over time.