The single and correct level of abstraction at the specification by example level – 5 rules to write good examples

In one of my previous posts, I wrote about the single and right level of abstraction. In that article, I focused on the code and unit tests level. However, the single and the right level of abstraction principle applies also at a much higher level – at the level of specification by example. The driving force behind it is different than in the case of unit tests though, so it’s worth a separate discussion.

1. Write specification to engage stakeholders

The main driving force behind the specification by example is discussion with stakeholders. The primary reason for formulating requirements as examples is that such a form facilitates dialog. We don’t treat specificaton as something closed that stakeholders are able to provide in a perfect, final form in the first shot, but rather as something that will be iteratively, and what’s most important cooperatively refined.

The examples are really good at it, because they describe concrete, narrow cases, not complex and abstract policies. They are straightforward questions, asking how the system should behave in a particular situation, and it’s much easier to answer such questions that to validate the whole complex algorithm all at once.

The examples provide many other benefits, for example they work nicely as acceptance criteria and lend themselves well to automation, but these benefits are byproducts. The focal point of the specification by example are stakeholders. This is important to remember, because all the other guidelines stem directly from this fact.

2. Hide implementation details

To really engage stakeholders, you must write the examples at the level of abstraction they understand and are interested in. Usually, this is the business rules and behaviors level, not the implementation details or interface elements level.

For example, such specification isn’t at the abstraction level appropriate for stakeholders:

Given I have "some item" in my shopping cart
    And I'm on the "delivery options" screen
When I click a "priority delivery" radio button
    And I click the "proceed to the checkout" link
Then I should be redirected to the "checkout" screen
    And the "total" input field should equal "some item" price plus the "priority delivery" cost

Stakeholders are not interested in radio buttons, input fields, links and screens. They think in business terms, like this:

Given I have "some item" in my shopping cart
    And I've chosen priority delivery
When I proceed to the checkout
Then my total price should equal "some item" price plus the "priority delivery" cost

This is often summarized as a “describe what not how rule” and it’s very similar to staying at the proper level of abstraction in unit tests. The difference lies in what determines which level of abstraction is correct. In the case of unit tests, this is determined by the layer of the system a tested class is the part of; in the case of specification by example, this is determined by the stakeholder the specification targets.

3. Give concrete examples (don’t generalize)

The goal of specification by example is, as the name implies, to specify how the system should work by giving concrete examples of its behavior instead of generalized descriptions. The key word here is concrete. If you make an example too general, it stops to be an example and starts to lean more towards the “traditional” requirements description again.

Therefore, the following examples are too vague:

Given I've bought only digital items
Then I should not be charged for delivery

---

Given I've bought some digital items
    And I've bought at least one physical item
Then I should be charged for delivery

While the more concrete examples are better:

Given I've bought a PDF book and an mp3 song
Then I should not be charged for delivery

---

Given I've bought a PDF book and an mp3 song and a computer mouse
Then I should be charged for delivery

This may seem contradictory to the “describe what not how” principle. However, making examples concrete by including business details is not the same as leaking implementation details. Implementation details make specification hard to understand and brittle. Concrete business examples enable discussion with stakeholders, which is the goal of the specification by example approach.

An example can be treated as a question for a stakeholder to answer: “Given such start conditions, when such an action happens, then we should expect such results – is it correct?”. It’s much easier to reason about questions like this when they are concrete. Vague, generalized algorithms or policies leave a lot of room for hidden edge cases that the stakeholder can miss.

But how detailed the examples should be? In the example above, when we discuss a customer buying a book, is it enough to specify it’s an ebook? Or do we have to be more precise and say it’s a PDF book? Or should we provide the book title, author and ISBN number?

This leads us to another important observation:

4. Use the abstraction level relevant to a given stakeholder

A project almost always has many stakeholders, operating on different levels of abstraction, looking at the system from different perspectives. In order to implement the system correctly, you need to have a dialog with all of these stakeholders.

In effect, you’ll be writing examples targeting different people. As discussed in the previous section, to get the most value out of the discussion, the examples need to be concrete – they need to contain relevant business details. But here’s a catch: for different people different details are relevant. There’s no single abstraction level suitable for everyone.

Consider a sales representative and a CEO, reading examples regarding “trusted” customers. The CEO will likely make decisions about high-level policies, like below, himself:

Given a Trusted Customer
When he delays with payment
Then he should still be able to make new purchases

Given an Untrusted Customer
When he delays with payment
Then he should not be able to make any new purchases

On the other hand, a CEO will likely delegate decisions about low-level details, like below, to a domain expert from the sales department:

Given an order with a bank transfer form of payment
When the current date is 7 days from the purchase date
    And there is still no payment received
Then the payment should be considered delayed

Given an order with a credit card form of payment
When the current date is 2 days from the purchase date
    And the purchase week day is Mon, Tue, Wed or Thu
    And there is still no payment received
Then the payment should be considered delayed

Given an order with a credit card form of payment
When the purchase week day is Fri, Sat or Sun
    And the wire transfer week day is Tue
Then the payment should be considered on time

Both the CEO and a sales expert need concrete examples to be able to reason about them, but the “concreteness” for each of them means very different level of detail.

For each example you write, you must be aware who you write this example for, and consciously select the appropriate level of detail.

5. Target only one stakeholder per example

Because, as I’ve just discussed, different stakeholders need a different level of detail, it seems obvious that trying to involve more than one stakeholder in a single example can result in a messy description, mixing high and low level details, like this:

Given an Untrusted Customer
    And an order with a credit card form of payment
When the current date is 2 days from the purchase date
    And the purchase week day is Mon, Tue, Wed or Thu
    And there is still no payment received
Then the customer should not be able to make any new purchases

The above example is unnecessarily complicated and has poor readability. However, this isn’t the biggest problem. As I mentioned at the beginning of this post, the primary reason to write requirements in the example format is to facilitate discussion with stakeholders. With the above example, such discussion is hardly possible.

You can’t ask CEO if this example is correct, because he’ll get lost in the details of how bank transfers’ processing may be delayed on weekends etc. These are operational details, that should be well understood by specialized domain experts, but are irrelevant at the CEO level. You can’t ask the sales department domain expert if this example is correct either, because although he will understand all the low level details, he’s not in position to validate if the decision about the company strategy towards trusted and untrusted customers is correct.

To fully validate this example, you’d have to gather both the CEO and the sales domain expert in the same place and at the same time and have them work out the answer together, which makes discussion less effective and more difficult to schedule.

Summary

Although it may not be immediately obvious, the same rules that govern unit tests (or clean code in general) apply also at the specification by example level. The difference lies in what motivates these rules: the ease of dialog with stakeholders instead of code readability.

It’s important to remember this relation between clean code principles and examples. Examples are often written by developers. By a slight shift of focus – thinking in terms of stakeholders instead in terms of architecture – developers can leverage most of the knowledge they already posess, utilizing rules like the Single Level of Abstraction Principle to write better, more robust examples.

How do you write your examples? Do you notice any other patterns that are similar to the code-related ones? Please share your experiences in the comments section below!

Advertisements

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s