Saturday, September 13, 2014

Clean Code With Builders

To anyone who says that software engineering is not an art I say "Have you heard about design patterns?". Design patterns are like poetry to a software engineer. I never meant to write a post about them because tons of posts and books were already published on those. But following a talk I gave this week I understood that people are not familiar with the ways the "Builder" pattern can help them create a cleaner code.




What Is The Builder Pattern?

You can skip this part if you're familiar with the Builder pattern for creating immutable objects.

The "Builder" pattern helps us, not surprisingly, to build objects. It is often used for building immutable objects. For example, let's say we have this class:


public class Student {
    private final String givenName;
    private final String lastName;
    private final int averageGrade;
    private final int age;

    public Student (final String givenName, final String lastName, 
                    int averageGrade, int age) {
        this.givenName = givenName;
        this.lastName = lastName;
        this.averageGrade = averageGrade;
        this.age = age;
     }

     // Rest of the class is only getters with no setters here
}        

Then, if we want to split the assembly of the fields from the actual construction, we can use a builder in the following manner:

public class Student {
    private final String givenName;
    private final String lastName;
    private final int averageGrade;
    private final int age;

    public Student (final String givenName, final String lastName, 
                    int averageGrade, int age) {
        this.givenName = givenName;
        this.lastName = lastName;
        this.averageGrade = averageGrade;
        this.age = age;
     }

     // Rest of the class is only getters with no setters here

   public static class StudentBuilder {
 
     private String givenName;
     private String lastName;
     private int averageGrade;
     private int age;
        
     public StudentBuilder() { }

     public StudentBuilder withGivenName(final String givenName) {
       this.givenName = givenName;
       return this;
     }

     // The rest of the 'with' setters look the same...

     public Student build() {
       return new Student(givenName, lastName, averageGrade, age);
     }
  }
}        


This way we have a mutable inner class StudentBuilder which gathers the fields we need for the immutable class and when the time has come to create the immutable object we just call build() and we get the immutable object already fully constructed.

So what does that have to do with clean code?


Express Yourself With Builders

I'm sure you have seen a pattern similar to that in the past:
StudentGrades grades = new StudentGrades();
grades.setStudentId(97); // The student id in the DB
grades.setMath(84);
grades.setEnglish(92);
grades.setChemistry(75);
grades.setLiteratue(88);
grades.setGymnastic(55);
grades.setBiology(76);
grades.setHistory(81);

What we'll usually try to do next is something that will wrap these lines into a single line like this:
private StudentGrades createStudentGrades (int id, int math, int english,
                                           int chemistry, int literature,
                                           int gymnastic, int biology, int history) {
  StudentGrades grades = new StudentGrade();
  grades.setStudentId(id); // The student id in the DB
  grades.setMath(math);
  grades.setEnglish(english);
  grades.setChemistry(chemistry);
  grades.setLiteratue(literature);
  grades.setGymnastic(gymnastic);
  grades.setBiology(biology);
  grades.setHistory(history);
  return grades;
}
And indeed this will make the whole clutter of code into a one-liner:
StudentGrades grades = createStudentGrades(97, 84, 92, 75, 88, 55, 76, 81);
Well, this is great. Much less verbose. But without looking back on the createStudentGrades method, will you be able to tell what 75 stands for? Or 88?

This pattern is very hard to read. You have to scroll back and forth or at least open the tooltip of the method to understand what each number says.

Using the builder pattern can help us create something that is less verbose than the original version of the code (the one with the oh so many lines) and a bit more descriptive than this one liner. We'll create a builder for StudentGrades as follows:
// This doesn't have to be inner class now. It's not the immutability that we need the builder for. It's the readability
public StudentGradesBuilder { 
   int id;
   int math;
   int english;
   int chemistry;
   int literature;
   int gymnastic;
   int biology;
   int history;

   private StudentGradesBuilder() { 
     // Making the constructor private in order to enforce construction
     // with the readable static construction method.
   }

   public StudentGradesBuilder studentGrades() {
      return new StudentGradesBuilder();
   }

   public StudentGradesBuilder forUser(int id) {
      this.id = id;
      return this;
   }

   public StudentGradesBuilder withHistory(int history) {
     this.history = history;
     return this;
   }

   // Rest of setters

   public StudentGrades build() {
     StudentGrades grades = new StudentGrade();
     grades.setStudentId(id); 
     grades.setMath(math);
     grades.setEnglish(english);
     grades.setChemistry(chemistry);
     grades.setLiteratue(literature);
     grades.setGymnastic(gymnastic);
     grades.setBiology(biology);
     grades.setHistory(history);
     return grades;
   }
}

Now we can construct StudentGrades in a verbal, yet concise manner:
// Static importing of StudentGradesBuilder.studentGrades() method
// allows to call it directly which makes it readable like an English sentence
StudentGrades studentGrades = studentGrades().forUser(97).withMath(84)
                                             .withEnglish(92).withChemistry(75)
                                             .withLiterature(88).withGymnastic(55)
                                             .withBiology(76).withHistory(81)
                                             .build();

This can be read almost as an English sentence "studentGrades for user 97 with math 84...."

Don't know about you - but I really like it this way. This make me feel I read a sentence rather then trying to decrypt the meaning of a random group of numbers.


Find me on Twitter: @AviEtzioni


More interesting posts from this blog:

Friday, September 5, 2014

Elephant Carpaccio - Use Case

In my previous post I talked about a technique called "Elephant Carpaccio" for splitting large projects and epics to smaller, measurable and valuable stories and tasks.

I would like to share now an example for a real-life scenario and show how to apply the carpaccio technique for this scenario.


Our example case

Let's say we're working on an enterprise product. We started with one language in our UI - English. And now the company's salespersons say we're missing great deals due to our lack of support in a multi lingual interface. And now your PO asks your team to implement this new feature.

Let's try to think about this feature request. Where do we start? What is most important? And one of the most important questions of all - how long will it take?!

It's obvious we can't just run and implement such a feature because it probably requires some infrastructure to support a generic addition of new languages and most probably this addition of a new multi-language support will require a lot of changes in a lot of places.

Ask questions

A good start I found useful for me is to first ask questions. Many questions. It will be very beneficial to include a few people (at least the team and a product representative) in the discussions and let everybody ask questions. Here are some questions that you will probably want to answer before running and implementing the feature:

  1. Will the users of the product be able to change the language whenever they want? Or is it set on the system level for all users?
  2. Where in the UI will the user change the language?
  3. Do we need to support also things like error messages in our translations? Or is it ok to leave them in English?
  4. Should we support any RTL (Right-To-Left) languages?
  5. If we support RTL languages - should the entire UI be viewed in an RTL direction?
  6. Should the language selection be persisted or is it ok to always start a user session in English and allow the user to change?

Make assumptions

Asking these questions will help both product and R&D to understand where the value for this feature lies and what can be delayed for a later version/sprint.

After the value is clear, you can prioritize and create user stories. Each user story must deliver some value to the user and be estimated according to the efforts and risks that are assumed to be put into it:

Story #1 - Support Spanish in a specific UI menu:

    1. DoD (Definition of Done): The user will have a language selection list. When the user selects Spanish, a specific menu will be changed to Spanish
    2. Assumptions:
      1. The language is not persisted and the next time the user will log-in the system will be in English again.
      2. Only one menu should be translated when choosing Spanish
    3. Story points - 8: This is a hard story - we need to create the infrastructure.
    4. Tasks:
      1. Infrastructure design. (1 day - net, after design review and discussions)
      2. UI addition (0.5 day - just adding the language list widget is easy. It can be done while waiting for feedback on the design)
      3. Implementing infrastructure - storage of language codes, maybe failover (if a message doesn't exist in Spanish we'd like to fall back to English), a generic API for converting message codes to locale strings, etc... (4 days)
      4. Change the UI menu to use the converter API (0.5 day)
    5. Value: After this story there's a robust infrastructure and an already working spanish menu. The value addition is huge both in the user/product experience it adds and the easement of now changing other places.

Story #2 - Support Spanish in whole of the UI (No error support yet)

  1. DoD: All UI texts will be in spanish
  2. Assumptions:
    1. No support for errors (exceptions)
  3. Story points - 5: This is not hard but requires a lot of tedious work of replacing any string to the translation API call. This also requires a lot of time from QA to make sure everything is replaced correctly.
  4. Tasks: Just replacing string with the translated text (2 days + 3-4 QA days)
  5. Value: After this story we can see a UI that's in a different language and get the main multi-language experience.

Defining other languages

After the previous stories are done we can, in fact, add languages in a very low development (and even QA) costs. If the infrastructure is built correctly it will require no more then adding a file or a DB records with the translated strings. We can now choose 2 methods for splitting the stories:
  • All other languages once - Due the easiness of adding a new language splitting each language to its own story would be an overhead so all the languages could be combined into a 2-3 story-points story. Better approach
  • Adding languages one at a time - in case other languages are not given in advance, splitting the stories into 1 story-points stories is also fine.

Future features

After we finished the main feature, and we no longer loosing deals it's much easier to prioritize the remaining features like translating error codes, and support RTL and so on. We lowered the pressure from the business side, we provided a huge amount of value.
If these features that haven't made the cut are important enough - they will be waiting at the head of the backlog stack. If not, they will be pushed down the backlog which probably means that they were not that valuable to begin with.



This example is of course very specific and was born out of former projects I worked on. I hope I managed to give you the idea of how to do such tasks. Mastering Carpaccio takes time, but will benefit you a lot.

Find me on Twitter: @AviEtzioni


More interesting posts from this blog: