The Lost Art of Functional Decomposition

This is not to bash the Diamond Kata.

This is to illustrate how we’ve gotten all lost in fractional thinking, optimising bracnhes of trees while being blind to path through the forest. So, in the Diamond Kata we have to draw a diamond shape of letters, like this one here:


What do the requirements tell us? How would we describe the task in a sentence, that we could even communicate to our customers? If we attempt to formulate our thoughts clearly, we can see that the diamond is made up of a…

All four stages appear, and will indeed turn out to be simple functions, so we can already see the diamond function as a straightforward composition of these four components. In a superior language, such as Q>>, it could look like this:

diamond_sequence >> diamond_diagonal >> diamond_mirror_h >> diamond_mirror_v

But I don’t have Q>> working yet, so I created the four functions in Ruby with a simple testcase each, that I later discarded, as I don’t test private implementation details, then created the diamond function as a compose operation and added some tests to verify the result. And so I saw no room/need/use for TDD at all, as after a minute or so thinking, there was nothing left to discover, no hidden structures to emerge, no design to drive.

Here’s the resulting production code in Ruby. I’ll omit the tests, as they’re trivial, and the documentation, because I’m lazy:

def diamond(max)
def diamond_sequence(max)
def diamond_diagonal(str)
  dashes = '-' * (str.length - 1)
  0.upto(str.length - 1).map { |i| dashes.dup.insert(i, str[i]) }.join("\n")
def diamond_mirror_h(text) { |str| str.chomp!; str.reverse.chop + str }.join("\n")
def diamond_mirror_v(text)
  text + "\n" + { |str| str.chomp }.reverse[1..-1].join("\n")

Take a look at the line in diamond, especially when in Q>>, and consider just how expressive it is. That sole line holds the entire logic, while the workers are all simple, independent, utility functions. Also note, how this decomposition was not achieved via TDD, but by clearly formulating and breaking down the problem statement. What’s more, this approach could even work in more complex situations, as the workers don’t need to be this trivial, only a strong separation of concerns is necessary (kind of SRP style).

If I look at the production code that resulted from the various TDD approaches, both incremental or iterative, by Alistair Cockburn, Nat Pryce, Emily Bache, Ron Jeffries, Sandro Mancuso, George Dinwiddie or others, nowhere do I see the four structural forces of the problem being this clearly separated and this transparently composed. It appears to me, that despite TDD promising focus on design, the solutions turned out to be more or less technically structured, with domain concerns taking second seat only.

While other approaches are also possible, a solution that claims to be good design should be similar to the one presented here, in that it must break the overall complexity down into individual, orthogonal, borderline trivial aspects. What’s of most concern to me, that none of the TDD based end products (that I looked into) ended up with such a clear decomposition, e.g. many are fighting with the complexities of letter and placeholder sequences that are only induced by not separating what I referred to as sequence, diagonal and mirroring.

And so I wonder.

How much should we be focusing on following the rules of TDD and how much on understanding the problem? Is it possible to fully address the domain design while following the TDD process? Why did none of the TDD based solutions separate the structural forces of the kata? Who is to say, the process of TDD will not obscure our sights of the problem domain in more complex situations, just as it seemed to do here? If there’s a risk of producing inferiour code with TDD, then why should we do it? And how representative is this kata?