Feb 23

The fail fast idiom is all about catching software errors at the earliest possible stage. The idiom is surprisingly often just neglected, as if developers loved to spend days tracking down obscure bugs which they know would just as well have been caught with a tiny bit of extra work in the first place.

Failing fast doesn’t mean that the number of failures increases. Rather, it means that the failures are not neglected and ignored, but found as soon as possible. For example, suppose that we call a function that returns a value, which in our subsequent code must then be below a certain maximum value:

int value = p->FunctionCall();
if ( value > max )
{
    value = max;
}

DoStuff( value );

Here, if the returned value is greater than max, it is set to max. DoStuff() requires that the value is less or equal to max. Now, this clamping may or may not be the intended behaviour. If setting the value to max is just a fail-safe in order to prevent DoStuff() from crashing if p->FunctionCall() returns an erroneous value, it is better to make sure already at this point that the value is indeed within specified bounds by adding an assertion.

Assertions are one of the foresighted developer’s best friends. There are a multitude of ways in which assertions are used, for example in Symbian code there are e.g. __ASSERT_DEBUG() and __ASSERT_ALWAYS() macros, for which you can provide a function to be called if the assertion fails. Then there’s the good old assert() macro from the standard C library, which is defined to write to the standard error output (prior to calling abort()) at least the asserted expression and the file name and line number where the assertion failed.

Where to put assertions, then? Usually there are quite a few places in code where a certain invariant must hold true. For example, there may be a member variable which must have a certain value upon function invocation, or else the results of the function are undefined. As a pseudocode example, imagine that we’re supposed to sell toys, and all their bells and whistles must be painted before they’re ready for shipment.

void Toy::Paint()
{
    for_each( bells_.begin(), bells_.end(), PaintFunc );
    for_each( whistles_.begin(), whistles_.end(), PaintFunc );
}

Let’s say that we add a new Toy to a vector container to be shipped to a retailer but forget to call its Paint() function. We now have a bunch of toys but, unbeknownst to us due to our negligence, one of them is missing paint from its bells and whistles. Uh oh, the delivery truck is here, and those guys don’t have all day. Better call the Offload() function to make some space in the warehouse:

void WareHouse::Offload( const std::vector<Toy>& toys )
{
    for_each( toys.begin(); toys.end(); ShipFunc );
}

Now we’re done with the toys, but one has slipped through without all the invariants checked. A good solution here would be to check in the Offload() function that we’re shipping out kosher items. Supposing that bells and whistles derive from a common class, Part, and that the base class contains a IsPainted() function to check if the part in question has been painted, we can add an assert to the Offload() function as follows:

void WareHouse::Offload( const std::vector<Toy>& toys )
{
    for ( std::vector<Toy>::const_iterator<Toy> iter = toys.begin();
           iter != toys.end(); ++iter )
    {
        assert( iter->IsPainted() );
    }

    for_each( toys.begin(); toys.end(); ShipFunc );
}

Here the assert will stop program execution to prevent shipping of unpainted toys. What we’re trying to achieve here is that already during development phase we are able to deal with code paths that result in unpainted toys being shipped. It’s better to make the problems manifest themselves as early as possible (to fail fast) than trying to find obscure bugs during the final, often hectic stages of an imminent release.

Leave a Reply

preload preload preload