3 ms·
John Ousterhout has a different take on this in "A Philosophy of Software Design": The problem with test-driven development is that it focuses attention on get
by agonz253 5y ago
John Ousterhout has a different take on this in "A Philosophy of Software Design":
The problem with test-driven development is that it focuses attention on getting specific features working, rather than finding the best design. This is tactical [as opposed to strategic] programming pure and simple, with all of its disadvantages. Test-driven development is too incremental: at any point in time, it’s tempting to just hack in the next feature to make the next test pass. There’s no obvious time to design, so it’s easy to end up with a mess.
One place where it makes sense to write the tests first is when fixing bugs. Before fixing a bug, write a unit test that fails because of the bug. Then fix the bug and make sure that the unit test now passes. This is the best way to make sure you really have fixed the bug. If you fix the bug before writing the test, it’s possible that the new unit test doesn’t actually trigger the bug, in which case it won’t tell you whether you really fixed the problem.
- BurningFrog 5y ago> * There’s no obvious time to design* Design time is the refactoring after your tests pass. That's when you can move your code around with confidence, since you can lean on your tests.
- pydry 5y agoThis works provided you didnt encode your design mistakes in the tests themselves. Then youve just doubled the cost of fixing that mistake. It's hard to overstate how common this is.
- Jtsummers 5y ago> This works provided you didnt encode your design mistakes in the tests themselves. Then youve just doubled the cost of fixing that mistake. If your design mistakes are in your code and your tests, they'll also likely be in your mental model and your documentation (if it exists). At which point you what? Throw it all away and start over? In extreme cases, but in others you do partial rewrites and refactors. Address the issues. But not having tests because they may encode your design mistakes is as foolish as not having documentation or comments because they may also encode your design mistakes. In a few years, you'll have a blob of code that, in the best case, is perfectly readable and comprehensible. But, in reality, is likely to fail at communicating the overall design intent and requirements. And so what if it doubles the cost of fixing the mistake? You made a mistake and it had to be fixed anyways. In the end, I've never found tests to cost more than they saved. A lack of tests has always led to higher development costs (primarily as measured in time to delivery). Regressions are ludicrously common without tests, which eats away at your time (and therefore adds to your costs) very quickly.
- pydry 5y ago>If your design mistakes are in your code and your tests, they'll also likely be in your mental model and your documentation (if it exists). At which point you what? Throw it all away and start over? Kinda, yeah. Or huge chunks of it. Thats what the OP said. I've done a lot of that. Was that a rhetorical question? >And so what if it doubles the cost of fixing the mistake? You made a mistake and it had to be fixed anyways. Well, then assuming you need 1 month to do a product iteration and 1 to do tests and docs on it and you need 5 iterations before landing on the "right" requirements to meet product-market fit, that requires 10 months to get to fully tested code doing the right thing with TDD and 6 without. What if your runway is 8? >In a few years, you'll have a blob of code that, in the best case, is perfectly readable and comprehensible. But, in reality, is likely to fail at communicating the overall design intent and requirements. I've been on both sides of this problem and I honestly think that this is much less dangerous than not iterating on requirements fast enough. I've bailed a company out of a massive technical debt hangover before. It's horrendous but it's not usually fatal. But solving the wrong problem? Not finding the right problem soon enough? That's usually fatal.
- MrMan 5y agoyes design mistakes cost much more to a project than broken implementation in my experience. broken code is harmless compared to a codebase that has to be rewritten after the true requirements finally reveal themselves
- thibauts 5y agoYou can move the code that hides behind the interfaces that you test, but you can’t redesign these interfaces which is the essence of design.
- agonz253 5y agoThose tests themselves may need to change if significant refactoring is involved. Doing so after the refactoring seems to defeat the purpose of using TDD in the first place?
- nybble41 5y agoRefactoring should not break your tests. You wrote them without knowing anything about the implementation, so how can they break when you change only the implementation and not the user interface, results, or side effects? (If you change any of those you've moved beyond mere refactoring and need to start the TDD process over with new tests.)
- agonz253 5y agoWhy the assumption that only the implementation needs to change, especially when significant refactoring is involved? The interfaces themselves may need to change when striving for a better system design. This is also part of refactoring.
- nybble41 5y agoIf you are changing interfaces then you are changing requirements (which pertain to those interfaces), which means you must change your tests. What you are describing is not refactoring: "In computer programming and software design, code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior."[0] The interface is an integral part of external behavior. With that said, what is "internal" vs. "external" behavior is… fluid. A large-scale refactoring might involve changes to internal components with their own interfaces and tests. The tests for the component being refactored should not be affected, but in the course of refactoring the larger component you might make more extensive changes—not just refactoring—to the smaller pieces making up its internal architecture. For that you would need to design new interfaces & requirements for the internal components, write new tests, and then iterate on the implementation until the tests pass, just as with any other TDD process. In the meantime you have the unmodified tests for the larger component to verify that your new internal architecture still satisfies those higher-level requirements. [0] https://en.wikipedia.org/wiki/Code_refactoring https://en.wikipedia.org/wiki/Code_refactoring
- nightski 5y agoPersonally I find this a curious observation. I honestly rarely do TDD myself, but find it very similar to functional programming. Learning Haskell the big thing I picked up from it was top down design vs. bottom up design. What you describe is more akin to bottom up design. But I find TDD and even more so functional programming promote top down design which is the "strategic" approach. But it just goes to show any technique (including TDD) is not going to force you into good habits. This needs to be learned with experience.
- ParetoOptimal 5y agoTop down design is great, but a hard sell since people like small wins and find it simpler to go bottom up. Then when they're at the top they don't know what better would have looked like ;) They get to be happy the whole time though.