29 August 2015

Introducing Brutal Coding Constraints

Last year I teamed up with Martin Klose to run a workshop at the Agile Testing Days. We knew that some really experienced developers would be there and aimed for an expert level workshop. We wanted a really difficult session, something that was hard, maybe even impossible to do. So we came up with the idea of Brutal Coding Constraints.

A constraint is an artificial challenge during an exercise designed to help participants think about writing code differently than they would otherwise. Some constraints are an exaggeration of fundamental rules of object oriented design and are applicable during your day to day work. The more extreme ones might still help you understand the underlying concepts of object orientation.

playmobil executionerBrutal Coding Constraints
The Brutal Constraints constraint is a composite constraint like Object Calisthenics, a combination of several constraints, some of them already difficult enough on their own. One particular combination that we like is
  1. No Conditionals, i.e. no if, unless or ?: operator. while can be used as conditional too and is not allowed as well.
  2. No Loops, i.e. no for, while, do or repeat until or whatever repetition constructs your language offers. Together No Conditionals and No Loops are sometimes called Cyclomatic Complexity One.
  3. TTDaiymi (TDD as if you Meant it), a very strict interpretation of the practice of TDD. This is optional, a "bonus" constraint for experienced developers. If you never heard about it, just ignore this one.
  4. No Naked Primitives, i.e. wrapping all "primitive" values, e.g. booleans, numbers or strings. Also general purpose containers like List, Map or Set are considered primitive. In extension all generic types of your language are primitive because they are not from your domain. A generic date (e.g. java.util.Date) is not from your domain even if you use dates, because it either does not define all methods you need or it defines other methods you do not need.
  5. No void, i.e. all functions must return something, methods with no return value are forbidden.
  6. Immutable, i.e. all data and data-structures must be immutable.
All six constraints are regular Code Retreat Activities so I will skip their further discussion.

Violating the Rules
When practising these Brutal Constraints with some kata, Martin and I were not able to find an implementation that would satisfy all constraints right away. We usually allowed violations in the beginning and refactored towards the constraints after the green phase. Sometimes we would leave a violation in for a few red-green-refactor cycles. It helped us to go through the list in each refactoring step to make sure we did not forget any constraint. It happened that we had put a condition somewhere in the code and forget about it - we are just that used to using conditionals and loop constructs. Because participants often ask for it, here is a list when to be strict about the rules. It is allowed to violate constraints:
  • temporary until you fix them during the next refactoring step;
  • temporary until you fix them after triangulating a solution, probably during a larger refactoring step after several cycles, e.g. after the third test;
  • if an used framework requires it, e.g. using a Runnable needs a void run() method;
  • if the testing frameworks requires it, e.g. JUnit test methods are void methods and @Parameterized tests need List<Object[]> which is a primitive container.
In general it seems easier to refactor later but that defeats the purpose of the whole exercise. We ordered the six constraints by some kind of priority or difficulty. If you have to violate a constraint, try to follow the ones higher up in the list at least.

difficultiesDifficulty
As I said before, Martin and I aimed for a really difficult exercise and I think we offered the only expert level session at ATD2014. And yes, this exercise was hard. The difficulty of each constraint was multiplied by their combination. For example I wrote about combining TDDaiymi, No Naked Primitives and No Conditionals last year. The attendees of the workshop agreed, "it was really difficult" up to "WTF" ;-). Brutal Coding Constraints are definitely too difficult for programming beginners, who even struggle with the concept of immutability.

An exercise like the Brutal Constraints can get frustrating easily. We told the participants that we made the session impossible on purpose, so they would not feel bad when getting stuck. When creating the workshop we got stuck ourselves several times, so we knew what to expect. During the session we paid close attention to the participants' mood and were prepared to offer hints on how to proceed without violating constraints. All participants worked hard and enjoyed the exercise.

Why practise like that?
When we prepared for the workshop we experimented with these constraints several times both in Java and JavaScript. It was difficult and interesting at the same time. I discussed some of our findings already. Also the ATD2014 participants liked the exercise. In the feedback round several people said that the constraints forced them to "think outside of the box" and that they liked the opportunity to "deviate from usual way" how they create software.

What about Functional Programming?
The Brutal Constraints focus on Object Oriented Programming. No Naked Primitives is the main driving force to create more types. On the other hand, constraints 1, 2, 5 and 6 might not challenge in Functional Programming. Instead of explicit conditionals some languages provide an Option or Maybe type and filter operations remove unwanted elements from containers. Most loops are unnecessary because containers provide map, foreach or similar operations. Also a recursive function call is not a loop. Pure functions and immutability are base concepts of functional programming anyway. I would like to see a solution following the above constraints in Clojure or Haskel. I am unsure how No Naked Primitives translates into Functional Programming.

Tic Tac ToeThe Assignment
In general the actual assignment, i.e. the problem that participants are asked to solve, does not matter for a kata but we wanted a problem that did not support the constraints. (Evil grin ;-) Such a problem would have a linear or higher dimensional structure with a need for looping (which is not allowed) and business rules, which are conditionals (which are not allowed either). We started with the classic Game of Life but it took us too long to reach a point where the constraints forced us to think harder. So we switched to a smaller problem, finding the winner of a game of Tic-tac-toe, which worked well for us.

Hints for Facilitators
If you plan to host your own Brutal Constraints exercise, your first priority is to help the participants to meet the constraints. It is easy to miss an if or a void method. We recommend printing the list of constraints and TDDaiymi rules as handout for each pair in the workshop. Second you need to pay attention to people's mood, as I described above.

For a short workshop it would help to force participants into situations conflicting with the Brutal Constraints as early as possible. One way to do this is to start with a prepared code base that already contains the first loop or conditional which needs to be removed. But then the list of supported programming languages for the workshop is small, ruling out less popular ones. Another way is to ask participants to follow a list of predefined test cases. While this allows any language, it impedes the creativity of solution finding. We still have to find a good list of test cases though.

Moar Brutality
But why should we stop here? We can make the exercise even more difficult. A suitable constraint to add is No Duplication, i.e. being very aggressive about duplication in the code. Unfortunately detecting duplication is less straight forward than following constraints 1, 2, 4, 5 and 6 which just deny certain reserved keywords or library classes. Another option is to add Baby Steps to force smaller working steps and Baby Steps has been combined with TDDaiymi already. When practising the constraints we committed every five to six minutes without problems.

Credits
Thanks to Martin Klose for creating the Brutal Coding Constraints with me. Pair facilitation is just so much more rewarding than solo work.

3 comments:

Unknown said...

The more I dive into functional programming, I have the gut feeling that these constraints listed above (or the Object Calisthenics) try to mimic, or recreate the features of, FP in OO. You listed how various points are implicitly solved when doing FP.

For instance, my pet-peeve to look for implementations without using conditionals (if) was solved when I stumbled over that fizz-buzz implementation using Lambda Calculus.

Does the current trend of FP also reach our practices as well?

Seb said...

Hey Christian, remember that you can also avoid ifs by old-school OOP polymorphism!

Peter Kofler said...

I definitely agree that FP also reaches our practice as FP gets more and more main-stream. But these rules also helped me to deepen my understanding of pure OO, esp. rules (3) and (4). And as Sebastian said, you can chose how to "fix" the missing if/loop, there are many possibilities.