Monday, January 02, 2006

Let code DRY on its own: Duplicate, Scan, Refactor, Abstract

Tonight I spent several hours trying to get a piece of code of the Intent framework working. As usual these mistakes tend to be due to the wrong kind of laziness and to a false perception of coding economies.

I had this set of classes handling 'facts' - a recording of certain chosen outputs of a piece of code - and I wanted to add a new feature called 'snapshots'.

When you create a snapshot all the outputs of your program that would go to stdout are implicitly redirected to a file where they are stored and later used for a pretty raw form of regression testing. Snapshots looked to me like a specialized form of Facts, so what I did was to inherit from my FactsEngine class a SnapshotsEngine class, and I started building on that.

However as I was halfway through my top-down implementation (can I test first a test first framework? :-) I started realizing that the workflows of the two engines were not as similar as they seemed to me from an initial high level view. There were a few annoying details that kept getting in the way. Some state that should have been initialized, some step that was skipped, some more steps that needed to be added.

Soon enough the SnapshotsEngine had become an unreadable patch of the FactsEngine, that also had to be 'abstracted' to allow hooks for the SnapshotsEngine. By the way, nothing worked anymore and I started downloading Komodo so that I could use a debugger.

As I am writing I start feeling that resorting to debugging is like an admission of failure. I tried to don't repeat myself by going for inheritance, but I did it a bit too early.

DRY - Don't repeat yourself - should be interpreted in a teleological sense. The code should drift to a DRY state. Starting out DRY is not always the best way to get it right, as it is far too easy to fall into upfront design and believe that you really know things that you don't know only because you are able to generate early abstractions.

I find that the following sequence works well to produce DRY code:
What I found out is that a DRY, but bad abstraction is much much worst than duplicate code. Duplicate code can still be abstracted in a clean way, and it can also be pretty easy on the eye even if it is quite verbose and repetitive. Code that has been badly abstracted is obfuscated and hidden behind ad hoc abstractions and technical tricks that make it unreadable.

So, don't DRY your code. Apply the DuScRAb sequence and let the code DRY on its own.

Comments:
Hi,

The steps that you have mentioned are essentially a regular TDD encounter straight after writing a test.

Duplicate => Do the simplest thing that could possibly work, run the test and ensure the code works.

Scan => Refactor and test.

Refactor => Refactor and test.

Abstract => Refactor and test.

The intent framework sounds quite interested. I'll hopefully give it a try as soon as I get some time.
 
Hi Abdel, I will soon publish the lates version with lots of little improvements and support for snapshots (it registers and plays back what you do)
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?