TDD ignores design; that is a frequently stated misconception. Many people get this idea from the code focus of TDD. TDD does not call for the creation of any non-executable design documentation. So the questioning developer gets the idea that there is no design. But I say, “yes there is”.
TDD’s lack of narrative text or UML documentation should not fool you into believing that there is no design in TDD. There is plenty of design; design is continuous in TDD; design is not an event that has an end (unless you’ve stopped supporting the product). Though it is accurate that skilled test-driven developers do not generally create a lot of detailed up-front design. But there is plenty of detailed design documentation created by the skilled test-driven developer: the tests and the well structured code. The working example usages found in test cases is an excellent form of detailed documentation.
The activity of TDD is not passive about design; it detects design problems; it guides to potential solutions. In this article I’ll describe how TDD is design rot radar. In the next couple posts, I’ll show how TDD is a guiding beacon for good design, and how TDD relates to the big picture or design vision.
Design Rot Radar
Design is essential. It is so important that TDD practitioners don’t just do design at the beginning of a development effort. They do design every day. TDD encourages good design at the detailed level through encouraging loosely coupled modules with high cohesion. These are a natural outcome when you test-drive your code. Let’s see how.
Your “well designed” systems that have spawned one thousand line functions could have benefitted by TDD’s code centric view with its design rot early warning system. Remember back when those functions and classes were reasonably sized?
As your code began to grow, you would have noticed that the candidate code changes would be hard or impossible to test. This would happen long before the code is carelessly transformed into out of control thousand line functions. The “I can’t test this” blip on the radar is a warning of code problems to come.
Unlike the Not Invented Here feedback from the late-cycle review, test-driving gives you unambiguous design feedback in real-time. In time to do something about it well before it is too late. You just have to listen to the code and tests.
You see exactly where you could put the code for your current change, but you cannot get the test to exercise it. You have an urge to access the code’s private parts. It’s time to extract a new module or class.
As dependencies grow, tests become harder to write and you find odd relationships staring at you from the test cases. This test-perspective is telling you of unmanaged dependencies or growing responsibilities. The skilled test-driven developer would refactor the design. Maybe they wall off some dependencies, creating a new interface, a more abstract design. Testing becomes simpler and the design more modular. Also, the test-perspective makes you consider the ease of use of an interface, long before a specific implementation can pollute the API with low level details.
Notice I said the skilled test-driven developer. It takes time to learn how to design the tests and listen to what the tests and code are telling you. You can wean yourself off dependence on too much design up-front. Do your design up-front, but then test-drive its implementation. Choose specific usage scenarios or test cases to build a walking skeleton of the design. Keep track of up-front design decisions that you later had to toss out or revise. Next time try a little less up front design. See if you can convince yourself of what many test-driven developers have convinced themselves of.
It is natural to be concerned that if you don’t get it right the first time, changing will be difficult and dangerous. Given the high cost of manual retest, you would be right to think it difficult, dangerous and irresponsible. But TDD changes that. TDD is designed to support code evolution. The parts that don’t need to change are kept from accidentally changing by the safety-net made of tests.
But make no mistake about it, it you can’t get your code into a unit test, it’s not well designed.