Monday, July 16, 2012

Readable Code

Code needs to be readable.  Readable code is debuggable, re-inventable and extendable.  But not everyone seems to know what makes code readable.

Occasionally, programmers will use comments to explain their code.  Comments certainly can help make a section of code understandable, assuming the comments are intelligible and up to date with the code.  But they in no way make code itself readable.

    Comments should only ever be used to say things that the code itself cannot be made to say.

In other words, you can use comments to explain why a piece code is there, but not to explain what the code is doing.  Yes, there are legitimate exceptions to that explanation, but again, those all fall under the first statement.

Mindful of the value of self-documenting code, many programmers create code like this:

AddressMapper.prototype.setInitialLocation = function(initialLocation) {
    this.initialLocation = initialLocation;
};
AddressMapper.prototype.setContainerElement = function(containerElement) {
    this.containerElement = containerElement;
};
AddressMapper.prototype.renderMapWithEffect = function(effect) {
    //magic!
};
// and so on...

After all, this is self-documenting, understandable code.  No one can accuse them of being opaque or obscure, right?  Beware the evils of premature optimization, right?  YUI 2 was like this, so it's ok, right? No, it most certainly is not OK.  And YUI 2 made me want to cry.

While long, descriptive names are indeed much better than inscrutable acronyms and abbreviations, they do not always produce code that can be called readable.  The example above, is unreadable because it is repetitive, mind-numbing, finger-cramping blather. When code gets repetitive like this, eyes gloss over and even stupid bugs find it easy to hide. Granted, they are likely to do that no matter what, but code like this does not help. And it gets worse, when you create APIs like this, the app code using it ends up just as obnoxious:

var addressMapper = new AddressMapper();
addressMapper.setInitialLocation('Portland, OR');
addressMapper.setContainerElement($('#portlandMap')[0]);
addressMapper.renderMapWithEffect(AddressMapper.Effect.SLIDE_DOWN);

Verbosity is contagious. It spreads. It infects. It sucks the will to live.

When reinventing a wheel, you are not designing an independent feature.  You are creating an API.  Yes, the wheel must be able to roll and so the raw, unadorned functionality of the code is your priority.  But ugly wheels make for ugly vehicles, and no one wants those.

Readable wheel code must avoid needless repetition and be designed to encourage readable app code.  Now, imagine this fictional plugin as a jQuery plugin:

var map = $.fn.map = function(address, effect) {
    return this.each(function() {
       render(this, address, effectFunctions[effect]);
    });
};
function render(el, address, effectFn) {
    // magic!
};
var effectFunctions = {
    slideDown: function(){...},
    ...
};

Which leads to user code like this:

$('#portlandMap').map('Portland, OR', 'slideDown');

<div id="#portlandMap"></div>

The repetition has dropped to tolerable levels, but it's not exactly a picture of readability.  Let's take it a step further and follow all the Rules for Reinvention:

var map = $.fn.map = function() {
    return this.each(map.render);
};

map.render = function(element) {
    var $el = $(element),
        address = $el.attr('address'),
        effectFn = map.effects[$el.attr('effect')];
    // magic!
;
map.effects = {
    slideDown: function(){...},
    ...
};

$(document).ready(function() {
   $('map').map();
});

<map address="Portland, OR" effect="slideDown"/>

At this point, the plugin code is not only more readable, but the app code has become pretty much ideal--declarative, minimal and extremely readable.  In the end, this should always matter most.  Sometimes, for whatever reason, there will be an irreducible amount of complex, ugly code.  In such cases, you should always strive to absorb that mess into your library and keep your app code clean and neat.

                           A readable API takes priority over readable internal code.

In fact, the lower "level" a library runs at, the less readability matters (and the more test code does!)  Since wheel code is just one small step away from app code, your wheels should still be highly readable wherever possible.

No comments:

Post a Comment