Archive

design

Should examples/tests/specs/whatever be DRY(Don’t Repeat Yourself)? I’ve been thinking (and talking and arguing) about the value of test names recently and whether they are just unnecessary duplication, but that’s the subject of a future discussion. This is about the actual content of your examples. So, should your examples be DRY?

In a word, no. DRY zealotry is a classic example of an over-adherence to Best Practices, which as James Bach argues are a bogus concept anyway. When you are writing software, including executable examples, your focus should be on clarity of intent. If the code could be any more obvious, you’re probably not done yet. And given that code is created once but read and changed many more times, it is healthy to develop a strong sense of responsibility to the guys coming along after you.

In tests, flow trumps DRY

The DRY principle says that the definition of any concept should appear once and only once in your code. This is an admirable aim in that if you have to change the behaviour of a Flooble, you want to be able to change it in one place and be reasonably confident that your change will apply consistently across the codebase. If there are multiple definitions of a Flooble, the chances are that not only will you not catch them all, but that someone before you didn’t catch them all and the multiple definitions are already inconsistent, and who wants that?

However, when you are using examples to drive your code – a process I call Coding By Example but which most other people insist on calling Test-Driven Development – there is another principle in play that I believe trumps the DRY principle. The examples tell a story about what the code does. They are the documentation narrative that will guide future programmers (including yourself when you come back to change this code in three months time and you’ve forgotten what it does). In this case, clarity of intent is found in the quality of the narrative, not necessarily in minimising duplication.

Some years ago I had my first experience of pair programming with Martin Fowler. That is, I had done quite a bit of pair programming, just not with Martin before. We were looking at some ruby code I had written test-first, and Martin asked to see the tests, “to find out what the code does”. Then he did a rather odd thing. He started moving the tests around. I had a few helper classes and utility methods in the source file, neatly at the end out of the way. He moved them up and dropped them inline just ahead of the first test that used them.

Madness! I thought – now the supporting code is all over the place! It really offended my sense of tidiness. But then I saw a pattern beginning to emerge. The test code was starting to read like a story. He would introduce these little methods and classes just before their one walk-on line in the narrative. It was quite an eye-opener for me. The test code flowed, and unfolded the story of the class under test. (I know I’m using TDD vocabulary – this was in my pre-BDD days.)

Go with the flow

The A-ha! moment for me was when I imagined reading a story book where the plot and characters had been DRYed out. Everything would be in footnotes or appendices. All the character descriptions, plot elements, subtexts etc. would be carefully extracted into fully cross-referenced paragraphs. Great if you are “reading” an encyclopaedia, not so appropriate if you want to get into the flow and find out what happens. You would be forever flicking back and forth in the book and you would very quickly forget where you even were in the story. In the words of the old joke, a dictionary has a lousy plot but at least they explain all the words as they go.

So here’s my challenge to you. When you read through a test case, or spec file, does the story unfold? Does it start by introducing the object’s most important responsibilty? Does it then introduce the edge cases in descending order of priority? If the test uses helper classes and methods are they tucked away at the end, or worse yet in an entirely different file that I am expected to “just know” is there?

Try to avoid using before blocks or setUp methods – especially in an abstract test class. Just call the method that does the setting up directly from the example. Don’t leave me to guess there might a magic method in a different class that is being invoked before the test even runs – that’s just not fair.

Also thanks to Mikel Lindsaar from the rspec mailing list for an excellent article about the perils of slavishly following DRYness.

[mikel]http://www.lindsaar.net/2008/6/24/tip-24-being-clever-in-specs-is-for-dummies

I was in a hotel in Stockholm recently and I noticed a bottle opener attached to the wall in the bathroom. There was a bilingual sign under it which got me thinking about the term “bottle opener” itself. (I was giving a talk about BDD the next day so I was already thinking about how language is used.)

It occurred to me that “bottle opener” is a great example of goal-oriented vocabulary. The device itself is actually a cap remover, and it only works on one particular design of metal cap. The reason I use it, however, is to enable me to get to the beer in the bottle. Hence “bottle opener” rather than “cap remover”.

The task is just detail

There is more to this than just linguistic curiosity. If you use task-oriented vocabulary it can cause you to focus on the means rather than the goal, which in turn can limit your options. My favourite example of this is the term “search engine”. Searching is the activity I have to do because I’ve misplaced my keys and I’m locked outside. What I want is a find engine!

Google realises this. When I type something into Google, it guesses what I’m likely to be trying to find, not what I happen to be typing into the box. If I type in “Stockholm map”, I’m likely to be looking for a map of Stockholm (first three results are actual maps – presented as pictures) or some information about the town itself. If I type “hotels Stockholm” I’m probably planning a trip there and voila! lots of useful results for the traveller. Other “search” engines do just that – they search, and produce lists of results. It’s then down to me to sift out the ones I care about to get me closer to my goal.

“Blur” on a problem

We talk about “focusing on a problem” in order to solve it. This is a task-oriented phrase. An alternative would be to stand far enough back that you see the problem in its proper perspective. If anything you are “blurring” on the problem – deliberately losing focus on the detail to see if any larger-scale structure emerges.

I often describe BDD as outside-in development. You start at the outside with an automated scenario, and work inwards, discovering services and collaborators as you go, until you’re done. With a legacy application it can be difficult to remain outside enough, or to get a good enough frame of reference for “done”. Blurring can help with this.

For the last six months I’ve been involved in restructuring and re-architecting a legacy code base. It’s been quite a major undertaking, and has involved a number of false starts and dead ends. (I’m planning to write it up as an experience report at some point, but given my current throughput of things I plan to write, don’t expect it any time soon.) During this project, I’ve often found myself struggling to choose between alternative strategies, or unsure of where to go next. In these situations I’ve found that stepping back and “blurring” gives me enough perspective for one of the alternatives to become “obvious”. In fact a couple of my teammates have picked up on this and will actually suggest it as an activity when we are pairing. “We’re thrashing here – let’s step back and start from the outside again.”

It could be as simple as asking “whose responsibility is this feature?” or “who is the actual client of this method call?”. You don’t need to know the answers – just verbalising the questions can give you enough “blur” to gain a better perspective.

Blur on time as well as space

Linus Torvalds recently gave a talk where he said the problem with source control isn’t branching, it’s merging. Again, by taking a broader perspective – in this case temporal rather than spatial – his insight is that the goal is a successful merge some time in the future, not the task of branching now.

As a final thought, while I was thinking about this I realised the term “behaviour-driven” contrasts with “test-driven” in a similar way. My goal as a developer is to deliver a system that behaves in a particular way. Whether or not it has tests is an interesting metric, but not the core purpose. “Test-driven” development will cause me to have lots of tests, but it won’t necessarily get me nearer the goal of delivering business value through software. So you can use goal-oriented vocabulary in your development process as well as your code to help maintain perspective on what you are trying to achieve.

_Props to James Lewis for helping me formulate these ideas. And for being really good at perspective._

I was lucky enough to attend the Software Architecture Workshop in Cortina recently. It was a three day workshop based around the idea of Open Spaces, which involves handing the asylum keys to the inmates and seeing what happens.

I convened a session called “What’s so hard about Event-Driven Programming?” to explore the experiences of the other delegates in designing, implementing and testing asynchronous, event- or message-driven systems. I took the position that actually it was all very straightforward as long as you followed a few basic principles. Another delegate, Mats Helander, took the opposing view that asynchronous, event-based systems could develop scary emergent behaviour that would lead to a world of hurt.
Read More

Follow

Get every new post delivered to your Inbox.

Join 142 other followers