Live data from Hacker News

TDD did not live up to expectations

blogs.msdn.microsoft.com

421–430 of 450 posts

Re: TDD did not live up to expectations

#421
Over 405 comments, I am late to the party here, especially since this is just my 2cents.

I think TDD "failed" largely for creative reasons. And it didn't actually fail.

The reason I was willing to use the word failed, in quotations, is that I do think that TDD is dead in the sense that it's a stick that can be used to beat people into submission. There was a long series of debates on youtube with DHH and a few TDD advocates, titled "Is TDD Dead", and it's funny that I think DHH largely won the debate considering that I believe the answer is, clearly, "no, TDD is not dead". TDD remains relevant and useful.

And yet, I think the TDD proponents suffered a severe setback in that debate, severe enough that I'd consider it a pretty bruising defeat.

Why? Because the debate showed that debate is reasonable. That the position that TDD is dead can actually be defended. Here's the thing - a lot of TDD proponents denied the existence of a legitimate debate. There was right, and wrong. Blog posts saying that people who even question the value of TDD should be unemployable, that TDD might rightly one day have the force of law behind it, that questioning TDD is the modern day equivalent of medieval doctors denying the importance of sanitary conditions and washing hands. That sort of thing. I think that by the end of the debate, there were too many cracks in the TDD argument to deny that not doing TDD may, in fact, be a good way to write software. TDD may not be dead (or even close), but that sort of browbeating certainly was put to rest.

Some of it was what DHH called test driven design damage. But the biggest one was creative. TDD may simply not work for the creative flow required for many types of software development. It's like, to contrive an analogy of my own, requiring a writer to outline the next page before writing the current one. It's just too disruptive. You can justify it a hundred ways from Thursday, but if people doing it can't write software as well as people who don't, TDD will lose.

None of this is to understate the importance of test coverage. But write some code, write some tests, repeat - yeah, I think that works. Trying to force everyone to do TDD through a campaign of shaming and intimidation was a horrendous fail. That's was the outcome of the youtube debates - TDD advocates actually did defend the practice quite well, but they fell far short of a standard that would mean DHH shouldn't be employable because he question TDD.

Perhaps not all TDD advocates were that extreme, but it was a strong enough faction in the TDD movement that I don't think I'm finding extremists and using them as a straw man. That sort of browbeating really was part of the TDD culture, and I think that even the good parts of TDD, the parts we should keep and even evangelize as developers, are harder to defend because of these early tactics.

Re: TDD did not live up to expectations

#422
post #321

Earlier quoted context omitted.

"Functional core, imperative shell" sounds quite dogmatic and not "getting it" is no reason to fear for anyone's code. Just like the OP article argued that TDD can become an all-solving hammer in the eyes of people, so can functional programming. In this particular case I can think of a whole host of applications where the core should definitely not be functional (and, for what it's worth, immutable state is not inhe…

I can't think of a single case outside of a systems or game development context (and a number even there) where anything I have written was improved by tightly coupling state mutation to business logic. Not one. Rather than trying to invoke "dogma" so very seriously, feel free to suggest one. But it's a really long row to hoe: if you are conflating state mutation and business logic, you have entire classes of errors…

You know what? I had prepared a longer answer addressing most of your opinions here, but I'd rather not[1] so I'll just say I disagree and leave it at that.

[1] After all, I can't conclusively prove an imperative codebase is more maintainable anymore than anyone can do it for a functional one so this would just be another entry in a 50-year-old flamewar.

Re: TDD did not live up to expectations

#423

Earlier quoted context omitted.

Sounds like you're doing the right thing. Note that not every one would agree that you are doing "true TDD" (due to your lack of "unit" tests) but that doesn't matter. What matters is that: a) you are disciplined in your commitment to quality b) you offload as much of the tedious, repetitive work to the computer as possible. Regarding unit tests and mocking, I have a similar opinion. I tend to test at the larger comp…

>Object-Oriented decoupling through dependency injection makes mocking a lot easier (and removes the need for mocking frameworks) I'm confused by this. In C++, I found that one of the only ways to effectively use mocking was via dependency injection. For us, dependency injection was introduced to enable TDD. That it had other good qualities was a bonus.

A fair question. Let me put it this way: in order to properly do unit tests, you need to decouple business logic from "integration code" (code that manipulates external systems). Dependency injection is one approach. However, with this approach the business logic still depends (now indirectly) on the integration code.

Another approach is to model business logic using pure functions. Instead of business logic calling the integration logic (directly or indirectly) you have the integration logic calling the business logic.

The consequence of this is that when you are unit testing your business logic you do not need to mock anything. It's simply a matter of checking input/output mappings.

See this series of articles for a deeper explanation: http://blog.ploeh.dk/2017/01/27/from-dependency-injection-to...

Re: TDD did not live up to expectations

#424
post #344

Earlier quoted context omitted.

It's worth noting that Kent Beck ("Father of TDD") himself said that TDD doesn't always make sense. For him, the important thing has always been having a tight feedback loop for determining if he was still on the right track. For instance, when he was working on an unstructured log parser, there was no way TDD would work because the logging stream didn't have a pre-defined structure. Instead, he developed a process w…

Do you have a link to when Kent Beck was working on that log parser?

The "Is TDD Dead"[0] discussion between Martin Fowler, Kent Beck, and DHH.

[0]https://martinfowler.com/articles/is-tdd-dead/

Re: TDD did not live up to expectations

#425
post #326
post #280

Earlier quoted context omitted.

That's not how you're supposed to do it. Write one test, get it to pass, repeat.

Right. In that, you still write all of the tests before the code. If you are objecting only to the rhetoric of "wall" of tests. That just depends on your size of units. Think of it more as hurdles of tests. :)

> write all of the tests before the code

Everyone is interpreting this as, "write 10 tests then try to get them all to pass at once". That is not how you TDD. You write one, then get it to pass, then write another.

Maybe you mean, "write the test before the code", but when you say "write all tests before the code", it's not interpreted the same way.

Re: TDD did not live up to expectations

#426
This looks more like "Misunderstood TDD did not live up to expectations." It's obvious from the article that tests were written after the code. That explains the excessive coupling in the code and tests being hard to write and constantly getting in the way of code development. This is not "test driven development". This is bolting tests on top of already (poorly) designed and implemented code. Tests are an afterthought. They did not drive the design. No wonder it doesn't work well. The high coupling in the code has to be repeated in tests and it's a rather painful and fruitless exercise. Have tests been written first, it would have been clear which design led to lower coupling: the one it's easier to write tests for.

Re: TDD did not live up to expectations

#427

Earlier quoted context omitted.

Sounds like you're doing the right thing. Note that not every one would agree that you are doing "true TDD" (due to your lack of "unit" tests) but that doesn't matter. What matters is that: a) you are disciplined in your commitment to quality b) you offload as much of the tedious, repetitive work to the computer as possible. Regarding unit tests and mocking, I have a similar opinion. I tend to test at the larger comp…

>Note that not every one would agree that you are doing "true TDD" (due to your lack of "unit" tests) I think he's doing it right in spite of what other religious zealots may say. In my experience, unit test driven development causes three massive problems: * Unit tests are intrinsically tight coupling. * Unit tests require you to build a model for most things that you are testing against (e.g. your database). Buildi…

> In my experience unit tests work adequately for domains that are heavy on complex logic and light on integration (e.g. writing a parser). In domains which are light on complex logic and heavy on integration they fail badly (e.g. most business apps).

There's a testing methodology we used at my last company called "Change Risk Analysis" or something. Essentially, the point was that you separate business logic and integration code, and you write unit tests for the business logic. There was a metric that combined the the cyclomatic complexity with the test coverage to produce a "risk" score for methods and classes, and all code (or at least all new code) had to be under a certain risk threshold.

I would also add that domains that are heavy on complex logic and light on integration are also highly amenable to more sophisticated verification methods, such as generative testing and model checking.

Re: TDD did not live up to expectations

#428

TDD failed for economic reasons, not engineering ones. If you look at who were the early TDD proponents, virtually all of them were consultants who were called in to fix failing enterprise projects. When you're in this situation, the requirements are known. You have a single client, so you can largely do what the contract says you'll deliver and expect to get paid, and the previous failing team has already unearthed…

This was always my issue with TDD. I never tried it, but I know that the way in which I have written software for more than a decade is "get a loose set of requirements, start digging, write some code, figure out what works and what doesn't, change things, repeat until done."

I never saw a way in which I could write a large number of tests up front.

Re: TDD did not live up to expectations

#429
post #422

Earlier quoted context omitted.

I can't think of a single case outside of a systems or game development context (and a number even there) where anything I have written was improved by tightly coupling state mutation to business logic. Not one. Rather than trying to invoke "dogma" so very seriously, feel free to suggest one. But it's a really long row to hoe: if you are conflating state mutation and business logic, you have entire classes of errors…

You know what? I had prepared a longer answer addressing most of your opinions here, but I'd rather not[1] so I'll just say I disagree and leave it at that. [1] After all, I can't conclusively prove an imperative codebase is more maintainable anymore than anyone can do it for a functional one so this would just be another entry in a 50-year-old flamewar.

You might not be "conclusive", but a cogent argument like "I'm adding classes of bugs that don't exist otherwise but I get X for doing so" should be pretty straightforward. If it exists. My contention is that it doesn't, but I'm always willing to change my mind. I'm pretty emphatic about this because I've spent twenty years learning that every other approach in common practice will eventually screw you catastrophically.

Re: TDD did not live up to expectations

#430

Earlier quoted context omitted.

>Object-Oriented decoupling through dependency injection makes mocking a lot easier (and removes the need for mocking frameworks) I'm confused by this. In C++, I found that one of the only ways to effectively use mocking was via dependency injection. For us, dependency injection was introduced to enable TDD. That it had other good qualities was a bonus.

A fair question. Let me put it this way: in order to properly do unit tests, you need to decouple business logic from "integration code" (code that manipulates external systems). Dependency injection is one approach. However, with this approach the business logic still depends (now indirectly) on the integration code. Another approach is to model business logic using pure functions. Instead of business logic calling…

>Another approach is to model business logic using pure functions. Instead of business logic calling the integration logic (directly or indirectly) you have the integration logic calling the business logic.

Yes, this is similar to what I did. I ensured business logic dealt only with data structures and algorithms, and knew nothing about the rest of the world. If it needed to report an error, it would call an interface class's "ReportError" function, and pass a string. It did not need to know if the error went to the console, a file, an email, text message, etc.

However, in our case, the communication is still two way - just separated via interfaces. The integration logic does call the business logic, but the business logic may need to do things like report the results, log interesting information, etc. So we still had to mock the interface class (i.e. mock the ReportError function).

But in the bigger picture - yes. Not allowing your business logic to know anything about the details of the integration was really helpful. In principle, our customers could have written the business logic for us.

Sadly, my team did not accept it so we never adopted it.

Post reply on HN