Programming - - By James Edwards

Comment-Driven Development

You’ve probably heard about Test Driven Development, a programming methodology which focusses on test cases as a framework for designing and developing code. You may also have heard of Behavior Driven Development, a more holistic approach which focusses on the behavior of an application before and during the development process.

Well let me tell you about my programming methodology — Comment Driven Development. Start by writing a comment, in plain language, that explains what a piece of code is supposed to do, and why. Then write the code.

Sometimes this is the most direct way to approach a problem; if it’s easy to explain but difficult to express in logic, the internal discourse you get from writing it down in this way can help clarify your thinking towards a logical solution. Conversely, sometimes it’s easy to write the code but much harder to explain it; in that case writing the comment can help you clarify the purpose of the code in such a way as to make it explainable to other people. The act of writing the comment is like a pre-cursor to documentation.

And indeed, there are times when I’m writing a comment and I find that my thinking on the code in question changes. Because the explanation sounds ridiculous, or because I have to explain a flaw or undesirable compromise. The act of explaining it to someone else — which is what a good comment should do — helps you to better understand it yourself, and sometimes that leads to changing or scrapping it entirely.

When I was writing my accessible drag and drop script, dbx, there were several occassions where I simply couldn’t work out what to do next — how to express in logic the behavior that looked and felt right. So I took to this technique, by beginning with an explanation:

//if - the direction of movement is positive; and
//	clone left/top plus clone width/height is greater than box left/top; and
//	clone left/top is less than box left/top
//or - the direction of movement is negative; and
//	clone left/top is less than box left/top; and
//	clone left/top plus clone width/height is greater than box left/top

Once I had that, it was simply a case of writing the code to implement what I’d explained:

if
(
	(this.positive
		&& cloneprops.xy + cloneprops.wh > boxprops.xy
		&& cloneprops.xy < boxprops.xy)
	||
	(!this.positive
		&& cloneprops.xy < boxprops.xy
		&& cloneprops.xy + cloneprops.wh > boxprops.xy)
)

Conversely, there were situations where I knew immediately what code to write, but I also knew that I’d quickly forget what it was for:

var sibling = dbx.getSiblingBox(this.box, 'nextSibling');
if(this.box == sibling || this.boxes[i] == sibling) { return; }

So in comes a comment to spell it out:

//look for a next sibling of this box
//and don't continue if there isn't one,
//or it's the same as the box we're inserting before
//so that we're not doing an action that would result in no change
//this filtering improves efficiency generally,
//and is necessary specifically to stabilize the animation
//otherwise the multiple unecessary calls would overload the animation timers
//and the result would be snap movement with no apparent transition

I’ve learnt the hard way how frustrating it is to come back on a script after time, only to have no clue how it works. Have a look at this example, for a chess analysis script I wrote many years ago:

sqR=new Array(vmr,vmr);
for(j=0;j<sqid[vmr][1];j++){
	if(sqid[(vmr-1)]&&(sqid[vmr][1]-sqid[(vmr-1)][1]==1)){
		sqR[2]=vmr-1;
		com(vmn,sqR[2]);
		}
	if(sqid[(vmr-9)]&&(sqid[vmr][1]-sqid[(vmr-9)][1]==1&&sqid[vmr][0]-sqid[(vmr-9)][0]==1)){
		sqR[3]=vmr-9;
		com(vmn,sqR[3]);
		}
	}

Don’t ask me what it’s doing — I have no idea!

So now I comment like it’s going out of fashion, with at least one line of comment for every line of code, and typically two or three. Sometimes my comments are mini-epics, like this monster:

/*
use offleft positioning instead of display/visibility,
so that the menus are still accessible to screenreaders
the height and overflow:scroll is to reduce the amount of rendered output,
which speeds up the onload process
(using clip or overflow:hidden would also hide them from screenreaders)
but there are rendering or positioning problems
in mac/ie5, old gecko and opera < 7.5
in fact it screws up opera 7.23 completely - parts of the browser UI freeze up (!?)
so the styles are hidden from opera < 7.5 using the html[xmlns] selector
http://www.dithered.com/css_filters/css_only/xmlns_attribute_selector.html
this means that it won't happen on pages without an xmlns attribute
in other words - on HTML 4 pages, any browser-based screenreader using Opera 7.5-8 won't get the submenus
opera 9 and other browsers aren't affected because all are included in later selectors somewhere
opera's own speech/reading capabilities are not affected either,
because it generates events that open the menus, resetting all of this
and afaik there isn't any other browser-based reader that uses opera
so the likelihood is that this hole won't affect anybody at all
nonetheless it's still unfortunate, but it's practicably unavoidable -
the only other way to differentiate opera versions like this is the selector:lang(xx) pseudo-class,
but then we'd need a new custom varible to specify the user's language code
the styles are also hidden from old gecko builds using the commented selector hack
and from konqueror < 3.2 using the backslash hack, because it makes the navbar collapse
*/

All of which documents a single line of CSS:

html/**/[xmlns] .udm u\l{position:absolute;left:-10000px;height:0;overflow:scroll;}

So, okay, this is all a bit tongue in cheek. But nobody will ever complain at you for writing too many comments! My colleagues have even complemented me on the quality and thoroughness of my commenting. Maybe it’s stretching the point to call this a methodology, but it’s still a good idea.

Sponsors