User Story is Worthless, Behavior is What We Need

Introduction

User Story is suitable for describing what user needs, but not what user does and how system reacts to user actions within different contexts. It basically gives product team a way to quantify their output and let their boss know that they are doing their job. As a developer, you can't write code from user stories because you have no clue on what is the sequence of user actions and system reactions, what are the validations, what APIs to call and so on. As a QA, you can't test the software from user stories because it does not capture the context, the sequence of events, all possible system reactions. User stories add little value to dev lifecycle. It only helps product team understand how much work they have to do eventually and it helps finance team get a view on how much money people are talking about. But to UI designers, solution designers, developers, they are nothing but blobs of highly imprecise statements that leave room for hundreds of questions to be answered. The absence of “Context” and “Cause and Effect”, and the imprecise way of saying “As a...I want... so that...” leaves room for so many misinterpretations that there’s no way development team can produce software from just user stories without spending significant time all over again analysing the user stories. Software, and the universe eventually, is all about Cause and Effect. The Cause and Effect is not described in a user story.

Unlike user stories, the “Behavior” suggested by Behavior Driven Development (BDD) is a much better approach because the format of a behavior (Given context, When event, Then outcome), when used correctly, lets you think in terms of sequence of events, where the context, event and outcome are captured for each and every action user or system does, and thus works as a definite spec for designing the UI and architecture. It follows the Cause and Effect model, thus it can explain how the world (or your software) works. It can be so precise that sometimes a behavior works as a guideline for a developer to write a single function! Not just the developers, even the QA team can clearly capture what action they need to perform and how the system should respond. However, to get the real fruit out of behaviors, you need to write them properly, following the right format. So, let me give you some examples on how you can write good behaviors for UI, business layer, services and even functions and thus eliminate repeated requirement analysis that usually happens throughout the user-story driven development lifecycle.

If you can force product managers to be 10% more precise in defining the requirements before passing it down the life-cycle, then you can save almost 30% of the total waste cost throughout the development life-cycle by saving time and cost in post analysis discussions, documentation and revisions.

User Stories to Behaviors

If you still live in the imperfect world where you get user stories dumped on your desk and you have to produce code from it, you need to learn to convert user stories into behaviors. Here’s an example:

As an anonymous user, I want to login on the homepage,
so that I can access my account details.

The above user story leaves the door for many interpretations. Give the above user story to a developer and then see how many round trips it takes between developer and product team and eventually between developer and QA to get this shipped to production. How does user login on the homepage? By going to a login page or the login box is embedded on the homepage? Is username in email address format or free form text? What do we show when the username is wrong or password is wrong?

So, you have to back this user story with acceptance criteria that will answer these questions:

Acceptance Criteria

Homepage must show a login box

System must validate username and password

Username must be in email address format

Password must be 6 characters and 1 numeric

System must show the dashboard when username and password is correct

System must show error if the username and password is entered wrong

First, you need to write some acceptance criteria to describe the environment user is in. You have to describe there’s a login box, it has username and password. Then you have to describe some user actions. How does user enter the username and password. Then you have to describe the system’s behavior how it reacts. All these are put as acceptance criteria as Assertive Statements that must result in True/False. This is a ridiculous way of describing user actions and system reaction to those actions. Software (and the universe) is all about Cause and Effect. This Cause and Effect is totally missing from user story.

If you convert this user story to behavior, it will look like this:

Given an anonymous user who has registered before and is on the homepage login box,
When user enters username and password on the login box and
clicks Login button or hits enter,
Then it should validate the username and password and
redirect user to the dashboard if the account is valid,
and it should show invalid username and password inside the login box
when the credentials are incorrect,
and it should update the last login datetime for the user in database.

Here you see the context (e.g., anonymous user, on homepage) is clearly separated and explained in the “Given” block, unlike user stories where the context is spread in some random order in acceptance criteria. Then the user actions are clearly explained in the “When” block, unlike user stories where the user’s action is partly described in “I want to...” part and then partially described in some of the acceptance criteria that you need to figure out yourself. The outcome, both positive and negatives are described in “Then” block in the behavior, which is equivalent to user story’s “So that...” and accompanying acceptance criteria. As you see, in one behavior statement, you can precisely define the environment or context user is in before performing the action, then user performing the action and then the system reactions to the action. The past, the present and the future sequence is clearly followed in a behavior.

Behaviors for the UI

Behaviors is a great way to describe UI requirements since it clearly captures the sequence of user actions and all possible system reactions. For example:

Given an account holder with some outstanding payments and direct debits setup,
When user goes to the Account Details page by clicking the link on the header area,
Then it should show the users’ account numbers and balances in a grid view,
and it should show balance in red if it’s a negative balance,
and it should show a list of outstanding payments in red,
and it should show a list of direct debits setup in a grid.

Give this behavior to a developer and the developer will scream with joy and produce beautiful code out of this. Compared to this behavior if you give the equivalent user story:

As an account holder, I want to go to Account Details page
so that I can see my accounts and their balances,
my outstanding payments and direct debits.

Acceptance Criteria

The account holder already has some outstanding payments.

The account holder already has some direct debits configured.

A grid view shows the account numbers and balances.

Negative balance must be in red.

A list shows outstanding payments in red.

A grid shows direct debits, if any.

It takes a lot more sentences to describe the same behavior. Moreover, the first two acceptance criteria, which describes the context, aren't really acceptance criteria. Since there’s no other way to put context in the user story, it has to be put somewhere on the acceptance criteria. Finally, there’s no sequence of events happening. It does not say how user goes to the accounts details page.

I use behavior to describe the UI requirements in my open source project Dropthings. It’s a Web 2.0 start page that renders widgets and allows you to customize the widgets. I can define the first user visit behavior in this way:

Given a new user,
When user visits the homepage,
Then it should show the default widgets.

I can then use this exact same behavior to write test code that automates browser, simulates the user actions and verifies the expectations.

The above test is written using xUnit, Subspec (xUnit extension) and WatiN. You can find hundreds of such tests in the project that shows you how you can use behavior to define the UI and then write code that automates browser, simulates real user actions and checks the output on the browser to find out if the behavior is satisfied. Dropthings is an AJAX web site. The test codes are rich in AJAX tricks and tips and should help you understand how to automated UI testing.

Behaviors for Services

You can use the behavior format to clearly define what input and output you expect from a service. For example:

Given an account holder with some outstanding payments and direct debit setup,
When GetAccountDetails method of AccountService is called with the account holder’s ID
Then it should return the account summary containing account names and balances,
and it should return an array of overdue payments,
and it should return an array of direct debits.

Now if you had to define this as a user story, oh boy:

As the Account Details page, I want to call GetAccountDetails of AccountService so that
I can get the account summary, overdue payments and direct debits.

In fact, it’s totally wrong. There’s no “user” here that will use the user story format. You have no choice but to use some other approach to convey such requirements to distributed component development teams. And the best approach is to use such behaviors to describe the behavior of the components.

Behaviors for Business Components

Just like services, you can describe behavior for Business Components. For example:

Given a user who has accounts with the bank,
When LoginManager.Login is called with user’s username and password,
Then it should return true if the username and password is valid
by calling Active Directory API,
and it should update the last login date time of the user in Audit database,
and it should throw exception if the username and password is wrong.

If you hand over this behavior to a developer, the developer can write the business components in his/her sleep. It does not need further discussion. It does not require some UML diagram that shows who’s calling who. It’s a super fast way to get requirements communicated to the developers. Moreover, if you are writing unit tests, then you can use this exact behavior to produce unit tests.

Here’s an example of creating a business layer component using a behavior:

The above code might look like a lot of code, but the point I am trying to make is you can produce business layer components out of behavior and you can test them using the same behavior.

Behaviors for Data Access Layer

You can even define behaviors for your database just like the way you do for services and business layer. If you have some SP that does some complex job, you can use the behavior format to define what the SP should do. For example:

Given a user in the aspnet_users table,
When AuthenticateUser is SP is called with the user’s loweredusername and password,
Then it should query the aspnet_users table to find a match,
and it should compare the password with the selected row in a case sensitive way,
and it should return the aspnet_user row if the username and
password matches successfully,
and it should return nothing if there’s no match.

Once you define the behavior this way, you can write the SP from it and then you can use some unit test tool to test the SP.

Behaviors for Functions!

If you have a function that does more than a trivial job, then you should use a behavior to explain what the function should do. For example:

Given a file in a local folder
When File.ReadAllLines is called with the file’s full path,
Then it should open the file and read all the content and return a string array,
and it should throw InvalidArgumentException if the path is wrong,
and it should return a null array if the file is zero length file,
and it should throw InvalidArgumentException if the path is a UNC or URL,

The above behavior is invaluable for writing unit tests. Once you get the function coded as per the behavior, then you can easily write a unit test for the function.

Again, here’s an example how I write behavior to explain what a particular function should do:

Here the behavior is explaining what GetTab function of TabRepository should do. I can then produce a unit test and use mocking frameworks like Moq to write a unit test for the function.

Conclusion

Behavior is an all-rounder solution to specify requirements for UI, services, business component, database, utility libraries and even for complex functions. The format encourages the requirement to be described in a precise way and leaves little room for confusion, when followed properly. Compared to user stories which can only explain user intentions, if behavior is used throughout the development lifecycle, it can greatly reduce repeated requirement analysis effort and can make the communication between product, design, development and QA team much more effective. If you can force product owners to be 10% more precise in defining the requirements, then you can save almost 30% of the total waste cost throughout the development life-cycle by saving time and cost in post analysis discussions, documentation and revisions.

Comments and Discussions

I think that this article is probably most suitable for those teams who are currently adopting agile. The problems that you are describing however, feel to me, very much like those of a development team in either the forming or storming stages of team development. If you step back a bit and take into account the experiences of many "newer" agile teams as a whole, you will realize that the problems that you are describing are in no way unique, and are in fact something experienced by most agile teams in the process of tailoring the overall system to meet their specific needs. (As prescribed by the methodology itself)

That being said, you need to take into account that the agile philosophy itself both welcomes and promotes the tailoring of the predefined processes, and as such this article is but one of many ways to flesh out the process itself into something more relevant to your own current circumstance.

So at this point let me surmise by saying that although I think your article offers both a unique point of view and some rock solid, well thought out ways to get there, I will only take from this what is applicable to my own current situation. (Which at this point is a fair amount!) And apply it to the areas in my current process where I can see that your advice would benefit my team as a whole.

Again that being said, very good article, relevant at least to myself and my team. If I could offer one suggestion, it would be that you have this article edited/moderated by someone whose first language is English, as this would do a lot for the overall legibility of the article.