Wrong generalization

TDD is also the process of evolving from concrete to generalized solutions. In the firsts steps you write code to build a simple behavior based on a simple example. The simpler, the better. As you go discovering more examples, the code evolves through refactoring to implement all those behaviors. So at the beginning it had maybe one conditional statement, but then another one came into place and eventually the conditional was replaced with a loop, which turned out to be the generic code to handle all those examples.

A typical evolution can be seen in the primer factors kata (you can see the evolution in the video). There is a point where the conditional statements must be changed for a loop in order to make all the cases work.

However, too much generalization makes the code hard to change when new requirements come along and also hard to read and understand. The question is ... where do we stop generalizing?

Generalize just enough to make your current requirements work. Once the functionality has been developed, stop transforming the code towards a more generic solution. It will not add value but rather make the code dark.

I've seen an example recently during a workshop. After a bunch of tests passing and some refactoring, we had two classes, one extending the other (in the production code). Both were solving the same problem but with some particular differences. It worked fine and all the examples were green. However, we saw that they were using a dictionary and that, part of their distinct behavior could be moved to the dictionary class, applying a subtle change in the code. With that change, we could transform the class hierarchy into a single class and delegate to the dictionary. That would be a generalization step which wasn't needed but let us save some lines of code.

The problem making this generalization is that code starts loosing its meaning in terms of the domain. The implementation is too complex and it is talking about dictionaries with operations. It is no longer talking about two problems in the domain that were slightly different. It starts loosing its extensibility. With the class hierarchy, we had the chance to create a new class extending the existing (Open Close Principle) but with an apparently unbreakable algorithm, there is no obvious way to extend the behavior.

Do not refactor if you are going to loose business semantic!