Friday, 15 October 2010

Don't dehydrate your code

(don't repeat yourself) is one of the first principles you learn as a fledgling coder.

When you build your first loop, you're embracing DRY, and all the wonderful code-shrinking that comes with it. And it feels good, and we're taught to seek out repetition as a terrible code smell, and do away with it as soon as possible.

But it's possible to be too DRY - no, really, it is.

When considering repetition for refactoring, there are two different questions we can ask:
  1. 'Are these two blocks of code the same?'
  2. 'Do these two blocks of code serve the same purpose?'

The different questions can lead to different answers for the same blocks of code. If the answer to question 2 is 'no' then you potentially create another, harder, refactor down the line, when you realise that the operation needs to change for one case, and not for another.

These too-dry refactorings leave their own particular code smell - optional parameters. They're not always a sign of overly-dry code - the robotlegs context takes an useful optional parameter for 'autostartup' - but when the optional parameter is littered through the code inside the function, it can be a sign that your code has become dehydrated and you'd be better off splitting it back out again - and either living with a little repetition or slicing the functionality up differently.

We make a similar call when we make decisions about inheritance and interfaces.

ClassB might have all the same functions as ClassA, and a few extras of its own, but unless ClassB truly "is a" ClassA, then there's no reason why the two should evolve together in the future. Better to tolerate the repetition because it correctly represents the cognitive model behind the application.

Similarly, unless ClassA and ClassB share obligations, and could potentially stand in for each other without your application logic losing sense, they shouldn't implement the same interface. Even if they have the same functions. Yes, even if they have the same functions.

Shut up with the "it's less code" thing

Of course all of this requires us to recognise that "It's less code" is never a refactoring justification in itself. Often, great refactorings create more code, not less. The objective is always to make your application easier to maintain, change and grow.

So - unless you're writing your code for a programmable calculator from the 1980s, any time you hear yourself think or say (or write, I see this a ton on the robotlegs support forum) "but it's less code this way..." just give yourself a little slap. There are often good reasons to take the fewer-classes approach, but they need to be more fleshed out than 'less code'.

The scarce resource is your brain. Your attention, your cognition, your working memory, your mental-models. And of course your time. An over-dry refactoring (particularly for the sake of 'less code') that requires a reversal later is expensive in terms of all these scarce resources.

Embrace (selective) repetition

A criticism of robotlegs is that you can end up with code repetition of very simple blocks of code in your Commands and Mediators, as well as a lot of custom Events. It *feels* weird to type the same code twice or three times to translate 3 different Events into the same corresponding action. But, in my mind, this is part of the power of the Command pattern.

Each Command encapsulates the logic steps required to respond to that situation - and they can freely change in future without impacting upon each other. The code repeated in your Commands is usually cheap and simple - if it's not then think about farming some of the logic out to a helper of some kind.

So don't sweat the 'glue' code having similarity between Commands and Mediators sometimes. Code that is dehydrated is just as tricky (sometimes trickier) to work with as code that needs a little DRYing off.


johnlindquist said...

I think it's a matter of truly identifying the problem you're solving and targeting it with specific code. Less code isn't a "solution", because, like you said, it could just cause more problems.

alecmce said...

Many thanks for expressing this idea so clearly. You are absolutely right.

I have been thinking recently about the proper separation of concepts in code - the most obvious example being the Point and the Vector (as in geometrical vector). At least in part, I think that these two classes were conflated because of a (mis)application of DRY principles. Computer science never recovered the distinction.

Though this isn't specifically what your article addresses, still the overlap between what you have written and what I have been contemplating has helped bring my thoughts into sharper focus.

Stray said...

@john - "truly identifying the problem you're solving and targeting it" = bang on I think.

@alec That's a really, really strong example of the ClassA looks-like ClassB conflation thing. Point and geom.Vector can look the same in terms of shared API. And yet they really, really shouldn't be mixed up.

They have no business sharing an interface or a class hierarchy - but sadly is exactly the kind of place where people do think 'DRY!' and get in a conceptual mess.

What flows from the poor mixing of the two is the possibility of writing code that compiles but is utter nonsense in Math terms. Like scaling a Point, or 'plotting' a Vector on a graph. Eek!

SlevinBE said...

My thoughts exactly. I also think "too DRY" can hurt the codebase more then it does good.
And as you said, less code isn't necessarily better. This rule can be applied to code style too, I prefere a more verbose code style because in most cases it makes it easier to understand the code.
btw, you have one of the most interesting blogs I've seen, so keep it up!

Stray said...

Thanks Slevin - that's nice to hear :)

I'm very much in agreement on the verbose code style.

Too much is better than too little when it comes to descriptive methods/properties I think.

I like a method that doesWhatItSaysOnTheTin(); !

nek said...

excellent example with Point vs. Vector.
Hope to see more of your "how to do it right" articles.