A lot happens from the time that a business owner envisions an idea in their mind and the time that the idea becomes software. The trick is getting through the whole software development process without losing the original idea.
Have you ever played that party game when you go around the circle whispering the same phrase and when you get to the end the phrase is completely different than when it started? Software development ends up like that a lot (especially when the business owner is changing the original idea!).
The problem is that we all speak a different languages. Developers speak one language, BAs another, PMs another, DBAs another, users another, business people another, executives another, and so on. So a large part of our job is learning how to translate what these people want into developer language (code), and doing it so that, in the end, the software speaks to them in their language.
Here’s a simple example:
Executive: “When we hire new employees, we need to make sure that they have a computer ready for them when they start.”
Business analyst: “User will enter the number of new employees on the screen. The system will check the inventory and make sure that we have enough machines, monitors, keyboards, and mice for the new employees. Each employee should have two monitors.”
Now it’s our turn. As developers, we have to do several translations in the process of writing the software. The DBA (or you) may have to design the database schema. You (the developer) have to write the code.
Remember, the goal is to not lose the original idea. That means that each time we “translate” the original idea, we need to do it little bits at a time.
Usually business analysts don’t give you specs that are written exactly how you want them. This is not a knock on BAs, but they speak a different language than us developers. What we really want from them is a set of acceptance criteria. In other words, we need to know what we have to do in order to complete the feature. Not only that, we need to know how we are going to test our feature so that we know that it’s working.
So let’s take the business analyst’s specs and translate them into acceptance criteria:
Given a stash of unused hardware
When a user enters the number of new employees
Then it should verify that we have one machine for each new employee
Then it should verify that we have two monitors for each new employee
Then it should verify that we have one keyboard for each new employee
Then it should verify that we have one mouse for each new employee
When we don’t have enough machines for new employees
Then we need to order new machines so that we have one for each new employee
When we don’t have enough monitors for new employees
Then we need to order new monitors so that we have two for each new employee
When we don’t have enough keyboards for new employees
Then we need to order new keyboards so that we have one for each new employee
When we don’t have enough mice for new employees
Then we need to order new mice so that we have one for each new employee
We’re not saying anything drastically different from what the business analyst said. But the way that we wrote it is important. Notice the use of the words given, when, then. Here’s the next translation:
public class Given_a_stash_of_unused_hardware
{
}
public class When_a_user_enters_the_number_of_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_it_should_verify_that_we_have_one_machine_for_each_new_employee()
{
}
[Test]
public void Then_it_should_verify_that_we_have_two_monitors_for_each_new_employee()
{
}
[Test]
public void Then_it_should_verify_that_we_have_one_keyboard_for_each_new_employee()
{
}
[Test]
public void Then_it_should_verify_that_we_have_one_mouse_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_machines_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_machines_so_that_we_have_one_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_monitors_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_monitors_so_that_we_have_two_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_keyboards_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_keyboards_so_that_we_have_one_for_each_new_employee()
{
}
}
public class When_we_don't_have_enough_mice_for_new_employees
: Given_a_stash_of_unused_hardware
{
[Test]
public void Then_we_need_to_order_new_mice_so_that_we_have_one_for_each_new_employee()
{
}
}
This is how you do behavior driven development. We took our acceptance criteria and wrote them out as code in the form of unit tests. These unit tests will prove that our code is working (when we get to that point) and it will also act as our documentation of what the code is supposed to do.
The reason that this is important is that we’re still in the middle of doing our translation. A lot of people take the tech specs and immediately start writing implementation code. But by doing that, you skip a step in the translation, and when you do that, you risk losing some of the original intent of the feature. This is one of the reason why writing tests before you write implementation code is important. First of all, if you write your tests first, then when they’re passing, you know that you’re done. Second, you’ve written out what the feature is supposed to do — nothing more, nothing less. This is going to help us implement the feature without losing the original intent of the person who thought it up.
Now, yes now, you can go write the implementation code. By focusing on what the end product should be and since you wrote your tests first, this part should be easy. It’s much easier to achieve your goal when you know what the goal is!