Posted by cbrink on Mon 30 Mar 2009 at 20:42
I just finished reading Refactoring - Improving the design of existing code by Martin Fowler. And it amazed me how much of it mirrors my experiences and my methods for refactoring. Both of us sync on all the same major points. I do have some minor issues with it but overall it's been great read. I think it speaks highly to the veracity of refactoring, as an integral way of improving code, that the same conclusions are continually reached independently. I highly recommend reading this book
My main complaint is the lack of emphasis the book gives to self-documenting your code. It does touch on the subject and has several refactorings for self-documenting code. But I think Mr. Fowler missed an opportunity to stress one of the most accessible, beneficial and least costly aspects of refactoring. If you skip most of the other patterns and just apply the Self-Documenting code patterns you will see a huge difference the the maintainability of your code.
The book is also a little light in the objection to comments. Comments should never be added unless there is no better way to self-document the code. They tend to be a crutch.
Martin Fowler uses the analogy of your code being a conversation with the machine and with the next developer. Comments speak to one and not the other. In the real world it would be called "talking out of both sides your mouth". Comments should never describe what the code is doing, that's job of the compiled code. When the code changes, you must change your comment. Right there you have duplicate code. Just because the comment doesn't speak to the compiler, doesn't make it second-class code or exempt from being called duplicate code. Comments should only be acceptable when something can not be inferred about the compiled code itself, and if you try to self-document your code these cases are very rare.
I haven't made a comment in my code in about the last 6 months. Well to be honest I have made several initial comments, but quickly found a way for the program to explain itself without making the comment. Here is an example (I have simplified this for brevity).
sub _is_good {
my $self = shift;
# run this test first because next test is expensive
if ( not $self->_has_good_title() ) {
print "Title of thing does not pass muster\n";
return;
}
if ( not $self->_has_good_body() ) {
print "Does not match\n";
return;
}
return 1;
}
sub _has_good_body {
my $self = shift;
my $external_content = Foo:Site->new( page => $self->page() );
return $external_content->body() eq $self->body();
}
I find it very likely that someone may want to change the order of tests. The comment supplies information that can not be inferred immediately from the code. If I were to simply rename the _check_body method and explicitly state that it was expensive, I would lose information about what the method actually did for the sake of explaining how long it took. So this comment seems like a good trade-off.
The problem is that the comment is disconnected from the actual operation and the operation may change. (Although it often is, the comment doesn't even need to be in a different method to have a disconnect from the actual code it is referring to)
So I need to find something that alludes to the cost of the method but still states the actual operation of the method. Here is my compromise.
sub _is_good {
my $self = shift;
if ( not $self->_has_good_title() ) {
print "Title of thing does not pass muster\n";
return;
}
if ( not $self->_has_good_body_external_check() ) {
print "Does not match\n";
return;
}
return 1;
}
The new method name now states the actual operation of the method and should alert the next programmer why these checks were separated in a 'short-circuit' manner. Later if the body test can be made less expensive with a local check or caching, I can add back the method _has_good_body.
There are two conversations going on when you write code: one with the machine and the other with the developer. Once you speak something to one and not the other you are going to have a disconnect. As with all communication breakdowns, this often will cause problems down the road. Having self-documenting code will help the next developer (which is often you) and reduce or remove the need for comments. If you want to put in a comment, consider it a red-flag that the code is not self-documented enough.
*cross-posted on Tampa Bay Perl Mongers*This article can be found online at the re/fact/or website at the following bookmarkable URL:
This article is copyright 2009 cbrink - please ask for permission to republish or translate.