3 ms·
That's one issue with TDD. I agree 100% in that respect. Another partly orthogonal issue is that design is important for some problems, and you don't usually r
by SomeCallMeTim 4y ago
That's one issue with TDD. I agree 100% in that respect.
Another partly orthogonal issue is that design is important for some problems, and you don't usually reach a good design by chipping away at a problem in tiny pieces.
TDD fanatics insist that it works for everything. Do I believe them that it improved the quality of their code? Absolutely; I've seen tons of crap code that would have benefited from any improvement to the design, and forcing it to be testable is one way to coerce better design decisions.
But it really only forces the first-order design at the lowest level to be decent. It doesn't help at all, or at least not much, with the data architecture or the overall data flow through the application.
And sometimes the only sane way to achieve a solid result is to sit down and design a clean architecture for the problem you're trying to solve.
I'm thinking of one solution I came up with for a problem that really wasn't amenable to the "write one test and get a positive result" approach of TDD. I built up a full tree data structure that was linked horizontally to "past" trees in the same hierarchy (each node was linked to its historical equivalent node). This data structure was really, really needed to handle the complex data constraints the client was requesting. As yes, we pushed the client to try to simplify those constraints, but they insisted.
The absolute spaghetti mess that would have resulted from TDD wouldn't have been possible to refactor into what I came up with. There's just no evolutionary path between points A and B. And after it was implemented and it functioned correctly--they changed the constraints. About a hundred times. I'm not even exaggerating.
Each new constraint required about 15 minutes of tweaking to the structure I'd created. And yes, I piled on tests to ensure it was working correctly--but the tests were all after the fact, and they weren't micro-unit tests but more of a broad system test that covered far more functionality than you'd normally put in a unit test. Some of the tests even needed to be serialized so that earlier tests could set up complex data and states for the later tests to exercise, which I understand is also a huge No No in TDD, but short of creating 10x as much testing code, much of it being completely redundant, I didn't really have a choice.
So your point about the design changing as you go is important, but sometimes even the initial design is complex enough that you don't want to just sit down and start coding without thinking about how the whole design should work. And no methodology will magically grant good design sense; that's just something that needs to be learned. There Is No Silver Bullet, after all.
- ivan_gammel 4y ago> Another partly orthogonal issue is that design is important for some problems, and you don't usually reach a good design by chipping away at a problem in tiny pieces. True, but… you can still design the architecture, outlining the solution for the entire problem, and then apply TDD. In this case your architectural solution will be an input for low level design created in TDD.
- SomeCallMeTim 4y agoYou can't always, though. I described a situation where TDD really, really, really wouldn't have worked. The whole structure needed to be developed, or at least 80% of it, before it would have made sense to write any tests--and the actual TDD philosophy would be to write "one small test" and only write exactly as much code as required to satisfy the test. The sane approach was to create the entire structure based on the design, and then test it after it was complete as an entire system. Some of the micro-functionality that TDD would have had you test would have become technical debt as a change-detector later when the client changed their specific requirements. As I said above, there is no evolutionary path from tiny pieces to the full structure, and TDD requires that you follow such an evolutionary path. If you're writing a bunch of tests and then creating a nontrivial amount of code, then you're following test-first, but not really following TDD. And I question even how valuable that is when you don't necessarily understand what would need to be tested before you've finished implementing the system.
- ivan_gammel 4y agoI disagree with you here. TDD does require evolutionary path for the entire system, but the minimum unit is a feature that is expected to be fully specified and implemented to pass the first test. You cannot evolve a data structure or an algorithm with TDD, because TDD by original definition allows only refactoring, not re-engineering (I.e. if your feature is addition, writing „return 4“ to pass test2plus2 isn’t meaningful TDD). So in your case properly applied TDD would require fully implemented structure or at least an atomic part of it within the known design to pass the first test (e.g. testing Feistel round function in an encryption algorithm is ok, but you will know the design of the entire algorithm from the start).