DEV Community

Discussion on: Improve your life with TDD

Collapse
 
mykezero profile image
Mykezero • Edited

I like this recent tweet from Ron Jeffries:

From his point of view, we must always keep design in mind at all times. This is regardless of using TDD or not using TDD. TDD might help with the design since the "refactoring phase" is really the thinking about design phase.

I've recently learned refactoring means not changing public API of a system: only the implementation should change. This might be what you meant by TDD only helping with changing the internals of a function.

fagnerbrack wrote a wonderful article on this exact subject:

Changing the API for something that is widely used through the system is hard, risky work. TDD helps us here, as a side benefit, of giving us a pretty comprehensive test suite that covers the entire system, since no functionality should be added without tests.

In these cases, one technique I use to improve the design of an existing system, is to write a new test using my "dream api". The API I wish I had.


We start with our existing API.

Sum(int x, int y) => x + y;

I might think my design is bad, and I'll start by playing around with the design in a new test:

Maybe this might handle future inputs better?

testSumDesign => Sum(Inputs: {1,2}) == 3

If I like this design, I now run into a new problem: this API is used in 3000 parts of the system.

To solve this problem, we'll incrementally switch existing consumers over to the new function.

Start by giving the new Sum function a different name: Sum2?

Sum(int x, int y) => x + y;
Sum2(Inputs) => Inputs.X + Inputs.Y

Now, in the existing tests, update all occurrences of Sum to use Sum2.

testSum => Sum(1,2) == 3

should be changed to

testSum => Sum2(Inputs: {1, 2}) == 3

The test should pass and we know Sum2 is compatible with every behavior that Sum implemented.

Now, you can begin the process of slowly transitioning every consumer of Sum to use Sum2:

AddThreeNumbers(1,2,3) => Sum(Sum(1,2), 3)

should be changed to

AddThreeNumbers(1,2,3) => Sum2(Inputs: {Sum2(Inputs: {1,2}}, 3)

After transitioning a few of them, I should pause and think about whether my new design is better or worse than the old one (In this case, I've actually made it worse: back to the drawing board...)

It may not be that TDD makes design suffer, but redesigning the API of heavily used components is painful work.

Hopefully that helps a little bit! ;