Composable Features

June 27, 2011

In Eloquent Ruby, Russ Olsen applies the composed method technique to a class. He says

"The composed method technique advocates dividing your class up into
methods that have three characteristics. First, each method should do a single
thing—focus on solving a single aspect of the problem ...

Second, each method needs to operate at a single conceptual level: Simply
put, don’t mix high-level logic with the nitty-gritty details. A method that
implements the business logic around, say, currency conversions, should not
suddenly veer off into the details of how the various accounts are stored in a
database.  Finally, each method needs to have a name that reflects its purpose.

When you apply this technique to classes you end up with classes with a relatively large number of methods each which is very simple. In applying this in detail I find that I create two sorts of methods.

  1. Methods that do something
  2. Methods that call other methods to do something

And in applying this technique I NEVER mix the two.

These techniques have been around for along time, as have a number of refactoring to get your code more composable. Use them and you'll never write another 20 line method ever again - personally anything over 5 lines sends of my alarm bells.

Anyhow if this technique is good for classes, might it also be good for features? And if it is how do we apply it?

A clue lies in how composed method technique helps eliminate proceduaral code. Perhaps we can make our features less procedural

    Feature: Title
      In order to encourage return visits
      As a site owner
      I want to support user accounts

    Scenario: login

    Procedural |                                  |  How   |
       ↓       |  When I go to the home page      |   ↓    | 
       ↓       |  And I fill in login with 'me'   |   ↓    | 
       ↓       |  And I fill in my password       |   ↓    | 
       ↓       |  And I press submit              |   ↓    | 
       ↓       |  Then I should be logged in      |   ↓    | 
       ↓       |                                  |   ↓    |
       ↓       |  When I go to the home page      |   ↓    | 
       ↓       |  And I login                     |   ↓    | 
       ↓       |  Then I should be logged in      |   ↓    | 
       ↓       |                                  |   ↓    |
       ↓       |  When I login                    |   ↓    | 
       ↓       |  Then I should be logged in      |   ↓    | 
    Declarative|                                  |  Why   |

Here we see a simple feature being refactored down to its bare minimum. The final feature format may make you uncomfortable

 When I XXXX
 Then I should be XXXX

seems to simplistic to be valuable. However in practice finding a good XXXX is really useful, and expressing basic functionality in this way is a great starting point for further exploration.

For example we can start dealing with failure very easily

When I login badly

instead of putting in fake credentials into a table.

So what do we get when we apply this sort of technique to a large set of features?

  1. A very succinct specification of what the application should do and why it should do it.
  2. No indication of how the application should do something.
  3. Features that are technically very easy to turn into step definitions
  4. Features that are very readable
  5. Features that have just the right amount of information

This last point is contentious. I know many people feel that features need to have more information in them. In particular the idea of features needing to have descriptive examples so you can explore how to do things is a strong argument. However I think this exploration and description is best done by implementation, review and iteration. If you iterate very quickly you don't need to explore the how up front, you can explore how things are done as you go along.

Why Would You Be Uncomfortable with This

Agile in general is not comfortable, and working in this way builds on that. This discomfort shows itself as

  1. the need to plan what will happen, rather than reacting to what is happening.

  2. the need to specify how things will be done, rather than provide feedback on how things are being done

  3. the need to micro manage rather than delegate responsibility

  4. the inability to trust