Einenlum.

Lessons learned about Bus Factor (Part 4/5): Stay close to the domain

Sat Sep 30 2017

Lessons learned about Bus Factor (Part 4/5): Stay close to the domain

This series of articles is taken from a talk I gave at the Berlin PHP Meetup. If you didn’t read the first one, here you go.

A painting from Max Ernst

The Entire City by Max Ernst

Last time we saw how we could refactor code in a simple way to make it more obvious. We did not talk yet about your client’s domain. Maybe we should have done that in the first place.

Stay close to the domain

When you create a website for a client, you initially don’t know their job. It can be about printing, banking, shipping stuff… whatever. You’re not an expert of these fields. Even if you have knowledge in those, every company has their own vocabulary, processes and issues. Your first concern, when building a great application that is maintainable, should always be to understand the domain of your client. Not only do you have to understand it, but it’s even better if you use the exact same words they use. This is called ubiquitous language, and it’s a common concept in Behavior Driven Development and in Domain Driven Design. Using the same vocabulary as your client will avoid you wasting a lot of time translating what they are talking about. If you don’t know Ubiquitous language, you can check this nice article presenting this concept. Here are a few quotes from it, that summarize the key points, in my opinion.

You can name your classes, methods, and variables anything. Why not use the terms that your domain experts use?
[…]
[Ubiquitous language] is reflecting in code how the users of the software think and speak about their work.
[…]
Programmers should speak the language of their domain experts, not the other way around.

Sometimes you will even realize that your client has not a clear vocab for a few things. Help them then, to find the best term to describe something and to keep always this same word as THE word. When you made all this work of understanding and translating your client language, please, write it down in a glossary. It can be in a Github wiki, or maybe better, directly in markdown pages put in the doc section of your repository. Write there, all the important concepts of your client. What is a retailer? What is a customer? How is defined an invoice? What defines a license and what is it used for? If your client does not speak english, write the matching between their terms and the terms used in the app (because yes, you will write your code in english. That should be obvious).

This is a really important tool to help a new developer to understand the domain and its implementation in the code. Actually, if you write explicit code and stay as close as possible to the domain, a salesperson with no coding knowledge should be able to read your code and tell you if you’re implementing the logic in a good way. It’s not always possible I got it. But that should be your ultimate goal.

Writing tests is also writing documentation

It’s quite obvious that tests will help you improve the quality of your code and avoid bugs. Great. But tests can also be a great way to help a developer dive into your project. The first thing I check when I dive into a new project, is the presence of functional tests. That can make you understand very quickly what this application is about and what it does. And then, if I want to understand more in detail what different classes do, I can check unit tests. As a PHP developer I like to use Behat (a kind of Cucumber-like for those who are familiar with it), a simple test runner allowing you to describe scenarios in a human readable language called Gherkin. Here is how it looks like:

Feature: An interesting feature for your app

    Scenario: A first amazing scenario
        Given I foo…
        When I bar…
        Then I should baz…

    Scenario: A second amazing scenario
        Given I foo…
        When I bar…
        Then I should baz…

Every feature is a list of scenarios, describing a specific user journey. Given and When statements describe an action. Then statements decribe an expected result (what we also call an assertion). Behat (or any Cucumber-like tool) will parse these *.feature files and launch your scenarios one after another, by translating all these Given, When, Then, into concrete code you implemented. Behat will do that thanks to PHP annotations, but the other tools and language implementations are very similar.

<?php

namespace features\Context;

use Behat\Behat\Context\Context;

class SomeContext implements Context
{
    /**
     * @Given I foo…
     */
    public function iFoo()
    {
        // Some actions
    }

    /**
     * @Then I should baz…
     */
    public function iShouldBaz()
    {
        // Some assertions
    }
}

These tools are just test runners that let you free to write very explicit and simple tests scenarios, that every human understands. This allows you to write these scenarios with your non-dev client when they ask you to do a new feature, and to help any new comer in the project to understand what the application is about. Cucumber-like tools and Gherkin are amazing to clarify your project.

The problem is that these tools allow you to do many many things, from scratch. Like manipulating a browser with already written Gherkin definitions. So usually, when looking at Behat tests, here is the kind of scenarios you could find in many projects.

Feature: Users can only order products they have access to

    Scenario: I cannot choose a product if I don't have access to it as a retailer
        Given I am on "/login"
        And I fill "retailer" in "Username"
        And I fill "loremipsum" in "Password"
        And I click on the "Login" button
        When I am on "/products"
        Then I should not see "Product 1"

Sure, it describes something. But it does not describe your domain. It describes a user journey thanks to a user interface, without really explaining the business rules.

First, since it is totally coupled to the user interface, what happens if you’re working on a foreign project? Maybe you will start to see these kinds of things:

Feature: Users can only order products they have access to

    Scenario: I cannot choose a product if I don't have access to it as a retailer
        Given I am on "/connexion"
        And I fill "retailer" in "Nom d'utilisateur"
        And I fill "loremipsum" in "Mot de passe"
        And I click on the "Connexion" button
        When I am on "/produits"
        Then I should not see "Produit 1"

Wuuuh. Ugly. As a french developer I can assure you it’s a quite common thing to see. But the most serious problem here, is that you lose the main topic of documentation you should care about: the domain of your client. Gherkin is a great tool, if you only describe the goal of the application in domain terms, without worrying at all about the concrete technical implementation. This scenario could be implemented with a classical website, a JSON Rest API, or a simple CLI. That is not our problem here. We want to describe where and why your mean of transportation is driving you. Not the colour of your car. Here is how we could describe our feature instead:

Feature: Users can order products they have access to

    Scenario: I cannot choose a product if I don't have access to it as a retailer
        Given I am a retailer
        And no product is available for my role
        Then I should not be able to order a product

How is it implemented? HTTP? Shell? Whatever. We don’t care on this level. Here you can really describe the business logic of your app, and check with your client if you really understood the additional value of their project. What’s even greater, is that it allows you to test the same scenario in different ways (end-to-end test with Selenium, infrastructure tests on the application layer…): these tools allow you to tag (@ui or @integration, for example) your features and scenarios and send these features to different layers of testing.

For those who want to go further, I really advise you the article “Introducing Modelling by Example by everzet (the creator of Behat) which explains much better and deeper what I just said (among other things). Also, if you want to know how it looks like to use Behat (or any Cucumber-like tool) the wrong way, I encourage you to read “Stop Simulating BDD, Write Tests” from Codeception’s blog (Codeception is another PHP test framework). Don’t get me wrong: I’m not saying Codeception is a bad tool or that Behat is necessarily better (I never worked with Codeception). I’m just saying the given arguments show a total misconception of the tool. This article shows the worst way to use Behat, so in the end we could say it’s a great article because it’s very instructive.

Staying as close to the domain as possible, and documenting the main goals of your app in a business language will help anyone to dive into your project and understand right away why all this has been built.

In the next (and final) article, we will think about how we can detect a Bus Factor issue as quick as possible.