D is for Documentation

Code is the way in which humans tell computers what to do. Lots of effort has gone into making code easier for humans to read in the form of high level languages like Java or C++ and scripting languages like PHP, Ruby, and Python. Despite mankind’s best efforts, writing code is still clearly an exercise for talking to computers. It has not evolved to the point where talking to a computer is as easy and natural as talking to other people.

That’s why documentation is so important. Programming languages are just a translation of a developer’s intent into something a computer can execute. The code may show the computer what you intended for it to do, but the context is lost when another developer comes back to it later. Computers don’t know what to do with context. If they did, the days of Skynet would already be upon us. Humans can process context and it makes the process of dissecting and understanding a computer program much easier.

I find it both sad and hilarious when I see a speaker evangelizing code without comments. Invariably, the speaker shows a slide with five lines of code and spends ten minutes explaining its form and function. Even the simplest and most contrived examples from some of the foremost experts in the field require context and explanation.

When a bug decides to show itself at three in the morning, in code that someone else wrote, context and intent are two very powerful tools. When bugs are found the question, “What was this supposed to do?” is more common than “What is this thing doing?” Figuring out what it is doing is easier when you have good log data to go on. Knowing what it was supposed to do is something only the original developer can tell you.

If you aren’t aware of the concept of Test Driven Development, I strongly recommend you dig into it. In summary, tests are written before the code to ensure that they code matches the business requirements. I would like to propose a complimentary development driver: Documentation Driven Development. By writing out the code as comments first, you can ensure that the context of the development process will be captured. For example, I start writing code with a docblock like this:

/**
 * Returns the array of AMQP arguments for the given queue.
 *
 * Depending on the configuration available, we may have one or more arguments which
 * need to be sent to RabbitMQ when the queue is declared. These arguments could be
 * things like high availability configurations.
 *
 * If something in getInstance() is failing, check here first. Trying to declare a
 * queue with a set of arguments that does not match the arguments which were used
 * the first time the queue was declared most likely will not work. Check the config
 * for AMQP and make sure that the arguments have not been changed since the queue
 * was originally created. The easiest way to reset them is to kill off the queue
 * and try to recreate it based on the new config.
 *
 * @param string $name The name of the queue which will be used as a key in configs.
 *
 * @return array The array of arguments from the config.
 */

Next I dive into the method body itself:

private static function _getQueueArgs($name)
{
    // Start with nothing.

    // We may need to set some configuration arguments.

    // Check for queue specific args first and then try defaults. We will log where we 
    // found the data.

    // Return the args we found.
}

After that, I layer in the actual code:

/**
 * Returns the array of AMQP arguments for the given queue.
 *
 * Depending on the configuration available, we may have one or more arguments which
 * need to be sent to RabbitMQ when the queue is declared. These arguments could be
 * things like high availability configurations.
 *
 * If something in getInstance() is failing, check here first. Trying to declare a
 * queue with a set of arguments that does not match the arguments which were used
 * the first time the queue was declared most likely will not work. Check the config
 * for AMQP and make sure that the arguments have not been changed since the queue
 * was originally created. The easiest way to reset them is to kill off the queue
 * and try to recreate it based on the new config.
 *
 * @param string $name The name of the queue which will be used as a key in configs.
 *
 * @return array The array of arguments from the config.
 */
private static function _getQueueArgs($name)
{
    static::$logger->trace('Entering ' . __FUNCTION__);

    // Start with nothing.
    $args = array();

    // We may need to set some configuration arguments.
    $cfg = Settings\AMQP::getInstance();

    // Check for queue specific args first and then try defaults. We will log where we 
    // found the data.
    if (array_key_exists($name, $cfg['queue_arguments'])) {
        $args = $cfg['queue_arguments'][$name];
        static::$logger->info('Queue specific args found for ' . $name);
    } elseif (array_key_exists('default', $cfg['queue_arguments'])) {
        $args = $cfg['queue_arguments']['default'];
        static:$logger->info('Default args used for ' . $name);
    }

    // Return the args we found.
    static::$logger->trace('Exiting ' . __FUNCTION__ . ' on success.');
    return $args;
}

The final result is a small method which is well documented and little if any extra time to write.

Armed with data from logs, unit tests which ensure functionality, configurations to control execution, isolation switches to lock down features, and contextual information in the form inline documentation, the process of finding bugs becomes easier. LUCID code communicates as if it were a member of the development team. It does all the things you expect from a coworker. It talks, it makes commitments, it works around problems and keeps a record of both what it is doing and why it is doing it.

2 Thoughts.

  1. Pingback: The Daily Six Pack: March 11, 2013 | Dirk Strauss

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>