Roman Suzi
3 min readJun 1, 2019

--

First of all, I am not against TDD. I am using it in some cases, where it really helps.

“…you already know the solution to the problem and all the possible test boundaries”. No, I am not assuming that. What I am assuming is prior to coding one should have gathered problem domain logic and have first approximation for the model (like mathematical model, equations, input/output data structures, etc) to “play” with. Having a model does not mean one has a solution. In other words, one needs an abstraction to start.

Unfortunately, for some reason, this post (and previous ones) does not do it. So most of the testing and refactoring efforts are lost in linguistical matters.

For example, instead of interestAmountForSecondRange everyone skilled in art will choose an interestAmountRange array. And the “mathematical” model for the problem becomes very compact when we abstract specific details. When abstraction is there, it’s easy to start writing tests/code, but it’s already completely different level.

The article is about “How TDD Can Prevent Over-Engineering” does not mean, that you mindlessly start with some test cases, produce some garbage, and then refine that through a series of refactorings.

“Over-Engineering is when someone decides to build more than what is really necessary based on speculation” (from your post). I can argue the current approach is exactly over-engineering by this definition: Without a good, compact, core abstraction tests and code are unnecessarily bloated.

Another example. Lets consider you want a function sum(x, y), but you need to only deal with x from [1, 2, 23] and y from [3, 5, 35]. Now, you can write a function with 9 conditions to test when x is 1 and y is 3 then result is 4, x is 1 and y is 5 then result is 6, etc, etc. Instead of just implementing it with + operation. However, for calculating function cos() quickly on a restricted CPU a lookup table may the solution to go, and then calculating cos() with thorough algorithm will be bad engineering.

My problem with your example is that it is not good to really illustrate over-engineering because it deals with lots of “linguistical” details instead of showing the more typical situations, where nearest (in the generalization scale) shortest abstraction is used, like in the above sum() example. In the same way using regular expression can replace hundreds of lines of code. Or using topological sort from the library can replace ad hoc implementation of tile ordering in an isometric game.

Please, note, my argumentation is orthogonal to the use of test-first, test-in-parallel or tests-after. I just want to say problem domain needs to be (mentally or using whiteboard or paper) explored before jumping into typing tests and/or code. TDD is not an excuse not to think about abstractions up-front.

Another point is some things are hard to test (or tests are called differently — stress-tests). For example, if you are developing sorting algorithm, you can easily develop some kind of bubble sort using TDD. But then if you also have a requirement to sort huge arrays, all you can hope is to use your tests as regression tests while you come up with your own quick sort algorithm. Not quite sure how TDD can lead you further in this case.

Hopefully, our discussion provide you with some ideas for future writings.

--

--

Responses (1)