Thursday, October 18, 2007

On clean code and refactoring

Bob Martin (also known as "Uncle Bob") visited us at BEKK today and gave us a short pep talk on "clean code". Using his argument parser written in Ruby as an example, he showed some refactoring tricks and some nice Ruby idioms (passing blocks as arguments, etc.). His main point was that you should never check in bad code and leave the fixing for later, cause "later" never comes. If you make a mess, you should clean it up, and clean it up now.

I've thought along these lines before, and one of the issues I've wondered about is: Should you refactor just after you write the code or should you leave it until the next time you need to change it? This is really just another way of asking "How do you know where your refactoring should end up?"

On one hand, I agree that you should clean up after yourself if you've duplicated something or in some other way made the code worse than it was. But on the other hand, you don't know now what the best structure is since you don't know what changes you want to make next time. You'll have to guess in what form you'll need the code to be the next time you change it. This might lead you to a "local optimum" of code quality, but when you revisit the code you might need to take in a totally different direction, forcing you to abandon the local optimum for a solution that is better in the big picture.

If you leave the code in its current (messy) state and wait until the next time you have to change it, it could be a lot easier to see in which direction you should go. With concrete requirements for how you want to change the code, you can probably see that "hey, this would be a lot easier to change if the code was structured this way...". You would then have to make the necessary refactorings, before making any changes.

Of course, there's always the chance that you'll be in a hurry the next time, and so you won't really have time for refactoring. Your changes will make the design worse, and all of a sudden you have a big ball of mud. Also, working this way feels a bit like always working uphill. For every change you want to make, you have to refactor something first. It's a bit like having to clean up the kitchen, when you really just want to make dinner.

Either way, I guess I prefer cleaning up after I've written some (messy) code to leaving it for later. You can probably make educated guesses in most cases as to where you should take the design. But, just as you should be ready to throw away some messy code, I think you should be prepared to back out some refactorings if they conflict with the way you feel the code should be going.

Anyway, just some food for thought.

1 comment:

Anonymous said...

IMHO, defining consistent components and providing well-thought protocols for your clients is mandatory (then, there can eventually be tradeoffs about the implementation depending on several factors such as time available etc).
If don't,your clients will in some respects encouraged to implement bypasses or misuse your components. Then, refactoring will be a real challenge because of the impacts on the code you don't control