User Stories are a great way to plan your work. You can take a big hairy requirement and break it down into chunks that are small enough to work on without anyone freaking out. When you’ve crumbled up your big hairy requirement into little user story chunks, you can pick and choose which chunk to build first, and even drop some chunks altogether when you realise they’re not that important. Great stuff.
So we like user stories. They’re our friends. Here’s one:
In order to pay for the items in my shopping cart, As a customer, I want a credit card checkout
Now, suppose when we do the analysis for this story we discover that certain types of credit cards are much easier to hook up to a checkout than others. We want to ship something as soon as possible, so that people can start giving us money. So we split the story in two.
One for the friendly FooCorp credit card:
In order to pay for the items in my shopping cart, As a customer with a FooCorp credit card, I want a credit card checkout that accepts my card
And another for the altogether-much-harder-to-integrate-with BarCorp credit card:
In order to pay for the items in my shopping cart, As a customer with a BarCorp credit card, I want a credit card checkout that accepts my card
As everyone knows, BarCorp have some really outdated systems, and connecting to their payment processing is a right old pain. We’ll leave that for later.
So let’s get on with implementing our first story. We’ve done some analysis on it, but let’s make sure we understand the acceptance criteria. We have a little sit-down with our product owner, and come up with this list:
- when a user attempts to pay with a BarCorp card, we show them a “Sorry” message
- when a user pays with a FooCorp card, we send them an email with their receipt
- when a user attempts to pay with a FooCorp card but gets their details wrong, we bounce the page and show them an error message, so they can correct their mistake.
- when a user attempts to pay with a FooCorp card but the card is declined, we bounce the page and show them an error message, so they can try another card.
So those are the acceptance criteria for this story. We didn’t do anything fancy with them like put them in a Microsoft WordÂ® document or a Jira ticket. We just wrote them down on a piece of paper.
Now we’ll use Cucumber to document these acceptance criteria as Scenarios that we can use to drive out the behaviour we need to get this story done.
Let’s start by creating a new feature:
Whoops. You made a mistake. Did you spot it?
That’s right, you went and got confused and thought that the user story you were implementing now was the same thing as the feature. It’s not.
Right now, this distinction probably seems a bit nit-picky. But wait until you’ve got around to implementing BarCorp’s credit card check-out, and BazCorp’s too. Your features directory is going to look like this:
features/ pay_with_foocorp_card.feature pay_with_barcorp_card.feature pay_with_bazcorp_card.feature
Can you see where this is going? Give it a few months, and your features directory is going to look like a history log of the development of your project. Hey, while you’re at it, why not put the jira ticket in the filename, or even use a tool to synchronise the feature files with Pivotal Tracker?
I see lots of people doing this, and trust me, it’s an anti-pattern.
Before there was Cucumber, there was the RSpec Story Runner. There was also this blog post. That was a long time ago. When Aslak created Cucumber, he renamed the files from
.feature. This wasn’t an accident or an idle act of whimsy: it’s because there’s a difference.
User Stories are a planning tool. They exist until they’re implemented, and then they disappear, absorbed into the code.
Cucumber features are a communication tool. They describe how the system behaves today, so that if you need to check how it works, you don’t need to read code or go punching buttons on the live system. Organising your features according to the way they were planned and implemented is a distraction from this purpose.
Imagine if the user manual for your washing machine was organised by how the washing machine had been constructed? A nonsense.
Going back to our example, let’s rename our feature:
mv features/pay_with_foocorp_card.feature features/pay_with_credit_card.feature
Now that’s more like it. As we implement the first User Story for FooCorp credit cards, we’ll put some scenarios into this feature. When we come to implement BarCorp support, we can add a couple more scenarios to the same feature. We might even have to modify existing scenarios, perhaps if we add a new widget to the UI to let the user choose their credit card type.
The User Story is absorbed into our features and becomes invisible, leaving the features as a live document of what the software does now, not as a record of how it was constructed.
Thanks to Paul Wilson for reminding me to write this post.
Good post Matt !! Look forward to looking at Cucmber some more on h2w.
Great post, Matt. Totally agree.
A question though To extrapolate on your example here, users can pay by Paypal. Would you add the feature pay_with_paypal.feature or amalgamate both into an overall payment.feature?
What I’m driving at is, what level of granularity do you prefer your features to be at?
I dunno, good question. I guess it would depend how different the behaviour for paying with paypal was to paying with a credit card. If you ended up with a feature with two distinct sets of scenarios – one set for credit cards, and another set for paypal – then I’d probably split the feature in two to keep it clear.
I’d also want to get a feel from my stakeholders as to how they thought about the behaviour – was paypal “just another method of payment” or something quite distinct?
So my answer is: it depends… 🙂
Nice post Matt.
Despite toying with Cucumber and Cuke4Nuke a few months back I still haven’t got a chance to use it “for real” so don’t have any proper experience to base my judgment on, but what you say makes sense to me.
Indeed, one of the things I had been trying to get in place where I work was an overhaul of how requirements are dealt with/ Traditionally large weight tomes of “the system shall” variety.
Initially we were thinking of exporting the stories we worked on from the tool we use, but the more I thought about it the more I realized this had to be wrong. That would give you something like a series of “transactions” that had led to the system you have…not a coherent description of the system.
Kind of splitting hairs here, but it would probably be okay to name your first feature features/pay_with_foocorp_card.feature, then once you have a second payment story you could refactor it out into a more general feature called features/pay_with_credit_card.feature. Since you’ve split the stories, it’s entirely possible that the other payment stories may never be delivered and leaving it to the last responsible moment means you have more information to name things correctly, etc.
Generally though, it’s about being vigilant and taking a step back and looking at things at a higher level when you start working on a new story.
Good point Todd. What I hope I’ve made clear is that it’s equally important to refactor your features as it is to refactor your code. Just as much as you want your code to look like you knew about all the requirements all along, you want your features to look like they were just written – you don’t want to see traces of how they got there.
Nice post Matt. Despite toying with Cucumber and Cuke4Nuke a few months back I still haven’t got a chance to use it “for real” so don’t have any proper experience to base my judgment on, but what you say makes sense to me. Indeed, one of the things I had been trying to get in place where I work was an overhaul of how requirements are dealt with/ Traditionally large weight tomes of “the system shall” variety. Initially we were thinking of exporting the stories we worked on from the tool we use, but the more I thought about it the more I realized this had to be wrong. That would give you something like a series of “transactions” that had led to the system you have…not a coherent description of the system.
I did it again. I started a new project and the very first feature I wrote was just, well, ugly.
Luckily I took a break to eat dinner because when I came back instead of going right back to work I took a few minutes Googling Cucumber and Aruba for some pointers on something unrelated.
And boy am I glad I did.
After reading your article I took one look at my horrible, horrible feature and realized I was doing it all wrong… again.
Thanks for the simple reminder.
Matt, what you wrote as a user story example after “Heres one” isn’t a user story is it? Isn’t that just how the description part of a feature is written in the Gherkin language?
Leave a comment