June 19, 2013
On Thursday, June 20, 2013 00:15:50 Szymon Gatner wrote:
> Writing the test before writing the function is exactly the point of TDD.

And that's precisely what I disagree with. I will _never_ function that way.

> It forces you to think about parameters it should take
> and value(s) it should return first.

Which you should be doing anyway. That's part of designing the class of function. I don't need tests to do that. The tests are to verify that what I wrote work correctly. That's it.

So, I think that it's clear that I will never agree with TDD. If someone else wants to code that way, and it works for them, great, but it's not how I want to function.

- Jonathan M Davis
June 19, 2013
On Wed, 19 Jun 2013 13:01:02 +0200
"Szymon Gatner" <noemail@gmail.com> wrote:

> This is not strictly D related but I am very curious about D's community opinion on the points made by non other than Jim Coplien here:
> 
> http://www.tele-task.de/archive/video/flash/16130/
> 
> D is the only language (that I am aware of) that has first class unit testing support. What do you think? Do we really just "mentally masturbate"?
> 
> Article about the myths of TDD referenced in the talk:
> 
> http://digitalcommons.calpoly.edu/cgi/viewcontent.cgi?article=1027&context=csse_fac


The main problem I have with TDD is that it's simply the latest form of ideology-oriented-development. It follows the same pattern as OO in the late 90's, and the popularity of Haskell and such a few years later:

1. Take a good idea (objects, functional programming, unittests, short iterative cycles, etc, whatever).

2. Decide "If using some of this is good, then forcing everything into this mold must be fantastic!"

3. Some die-hards sip the kool-aid, but the rest just adapt the good parts pragmatically, stripped of the ideological baggage.

4. The die-hards run into problems eventually forcing the more level-headed among them to admit "yea, turns out there are limitations to this alleged silver bullet after all". Meanwhile everyone else continues to just enjoy the benefits because they were sensible enough to only apply the idea in situations where it actually fit in the first place.

Unittests are great. Minimizing the amount of code changes in each iteration of your code-compile-test cycle is great. But there are *always* other concerns, and those concerns must always be balanced. For example:

- As people have said, some things aren't well-suited to automated
  testing.

- Sometimes you run into something that just *is* a big all-at-once
  change where splitting it into smaller tasks, while being ideal,
  would be impractical.

- Writing a unittest first forces the API to be designed before the
  implementation is written. But implementation is necessary to flush
  out unexpected design requirements (If you think you can always
  come up with an appropriate design and interface before
  implementing, then you're just plain wrong). Sometimes the
  appropriate design and API is obvious. Sometimes it isn't. When it
  isn't, then TDD skirts dangerously close to some of the problems of
  "Waterfall Model". Sure, TDD advocates refactoring-as-needed, but I
  can do that with or without TDD.

June 19, 2013
On 06/19/2013 10:59 PM, Jonathan M Davis wrote:
> It's not necessarily the case that you write _all_ of the tests and then all of the code, but as I understand it, with TDD, you write a test for a function before you write that function (though both could be written well before other functions and whatever tests go with them). And I completely disagree with the idea of writing a test for a piece of code that doesn't exist yet, even if you're going to write it immediately after writing the test.

My impression has been that in practice, in good TDD environments, it seems to work in a more co-evolutionary way than that, with code and tests being written fairly closely together.

I don't carry any particular torch for it, but if you do treat it as a co-evolutionary process, it dovetails quite nicely with some modern practices in industrial safety case management (the tradition was, prepare the design, then prepare the safety case, whereas modern practice is inclining towards developing the design and the safety case together).

> I _do_ agree with writing the tests fora function as soon as the function is done, in which case, you're likely going to have to do more work on the function, since it'll probably fail the test, and you'll probably improve the tests some more at that point as well. But I completely disagree with writing the test before the code, which is one of the key features of TDD as its always been explained to me.

Well, it occurred to me that with a bit more up-front testing thought, the bug in RandomSample that we discussed recently might never have happened -- it feels more like the tests are playing catch-up to the code, rather than the code to the testable design decisions.
June 19, 2013
On Wednesday, 19 June 2013 at 22:35:11 UTC, Jonathan M Davis wrote:
> On Thursday, June 20, 2013 00:15:50 Szymon Gatner wrote:
>> Writing the test before writing the function is exactly the point
>> of TDD.
>
> And that's precisely what I disagree with. I will _never_ function that way.
>
>> It forces you to think about parameters it should take
>> and value(s) it should return first.
>
> Which you should be doing anyway. That's part of designing the class of
> function. I don't need tests to do that. The tests are to verify that what I
> wrote work correctly. That's it.

Thing is TDD enforces such thinking. You are experienced programmer and you probably can figure out good API and proper implementation fast but young programmers can't. TDD forces them to came up with simple designs because otherwise they would not be able to pass own test!

Truth is it is not only about young inexperienced programmers. There are awesome implementation programmers out there. They will squeeze last bit of power from the cpu in tight loop but just can't seem to organize their code so that it is reusable. TDD is suppose to help them too.

>
> So, I think that it's clear that I will never agree with TDD. If someone else
> wants to code that way, and it works for them, great, but it's not how I want
> to function.
>

Anyway perfectly understandable POV. TDD is just a methodology. I myself not too long ago though it was *eghm* stupid to say the least. Why would I write the code entirely other way I was doing it until now? I am new to TDD but I must say I think it works for me. I am not ashamed to admit I am not to good in designing high level pieces of code. I know how to implement stuff but weeks/months later when requirements change I am always struggling with what i've done. Finally I see reusable pieces of code that I know I will be using in other possibly completely unrelated projects. Maybe TDD is just for stupid ppl like me? :P

June 19, 2013
On Wednesday, June 19, 2013 23:51:42 Joseph Rushton Wakeling wrote:
> Well, it occurred to me that with a bit more up-front testing thought, the bug in RandomSample that we discussed recently might never have happened -- it feels more like the tests are playing catch-up to the code, rather than the code to the testable design decisions.

Well, I think that if it's being done right, the tests are fairly complete before the code is ever commited, and there are plenty of cases where Phobos has pretty sparse testing. However, even if you have solid testing, you sometimes miss stuff and have to add further tests later.

Now, if what you're referring to is the issue where a function which used save used isInputRange in its template constraint, I would say that that was a fairly clear case of having inadequate tests before the code was submitted. But std.random is one of the older range-based modules, and we've come a long way since then (and we're still improving), so it doesn't surprise me at all if it's lacking some of the basic tests that it should have.

- Jonathan M Davis
June 19, 2013
On 6/19/2013 3:23 PM, Szymon Gatner wrote:
> Point of TDD for a square root function would be to create a good API for
> getting a square root of a number. Implementation is just a detail.

I just can't accept that. For one thing, implementation details often must drive the interface. Just writing specs without any knowledge of how it would be implemented will not produce an efficient design.

For the square root, there's a definite tradeoff between accuracy and speed. With no knowledge of those tradeoffs, and just coming up with a spec, how can you make the right decisions?

June 20, 2013
On Wednesday, 19 June 2013 at 23:44:29 UTC, Walter Bright wrote:
> I just can't accept that. For one thing, implementation details often must drive the interface. Just writing specs without any knowledge of how it would be implemented will not produce an efficient design.
>
> For the square root, there's a definite tradeoff between accuracy and speed. With no knowledge of those tradeoffs, and just coming up with a spec, how can you make the right decisions?

I don't think he's suggesting that it's not important how it's implemented. He's saying that as far as TDD goes, it's not concerned with those details. TDD is not going to solve all specification issues. Personally, I agree with you that square root functions and similar "low-level" APIs probably have a different set of needs to satisfy. Realistically, I don't think anyone would actually TDD something like a square root function.

Personally, I'm a big fan of TDD in general, but I think it's one of the most often misunderstood things in the field of programming. It's like if you ask a group of religious people to define what their deity wants from them... everyone's going to have a different response and everyone is going to claim that theirs is the "only one true way."

So, here's my "only one true way" of TDD (tongue in cheek):

TDD (Test Driven Development) has a terrible name. Too many people think it's about the tests. Tests are certainly a good side effect of TDD and I certainly can't suggest they don't have value, but the fact that tests are written is really irrelevant to what you are _really_ trying to get out of TDD. There's been numerous attempts to rebrand it (Test Driven Design, Behavior Driven Development, and Behavior Driven Design to name a few).

Ultimately, the "goal" of TDD is to have the programmer sit down and look at code from a USER'S (as in, user of the API) point of view _FIRST_. "Writing a test" as the first step is really just suggesting "as a user, how would you find it convenient to use this thing?" That's really just about all there is to it. It's one of those things that helps guide your thought process. If you're already doing that without writing tests first, then great. I've always found it pretty helpful to play around with how I want code to look like before I actually code the implementation details, though. More often I can come up with a simpler and more robust design after playing with how I want the end result to look like. As a cool bonus, once everything is done, you'll have a "specification" showing all of the features of a class/package/module and how everything is used. It will also show what kinds of preconditions are necessary for using those things (for instance, does your class need particular services to be online for it to work? If so, it'll show up in the tests).

The reason why a square root function seems pointless to TDD to me is because the whole reason you'd TDD it in the first place is to figure out how a user should use it. And as far as I'm concerned, if you have any ideas other than "sqrt(some floating point number) -> returns a floating point number of the same precision as the argument which if multiplied by itself is (nearly) the same as the original argument" you're likely to get a few strange looks. That problem is solved. The only questions that might remain is "which package should this be located in?" which isn't really a question that TDD is meant to solve.

TDD gets more useful once you start designing a larger system (especially if you're not experienced with designing a bigger system). If you're a fan of SOLID or some of the other principles of design, you'll likely see that code developed in TDD will naturally give rise to some "good practices". For instance, a major one is IOC/DI. It's much harder to write "testable" code that doesn't use DI or some technique similar. If you're using static classes/singletons you'll almost certainly have a lot of trouble. More often than not, I see people claiming something isn't testable when they're using things like singletons or shared resources that really don't need to be shared in the end, but using TDD would have highlighted that issue in the first place. That said, if you're experienced you would have probably not made that mistake, so maybe it's not that important for everyone.

Anyway, TDD isn't BS, but I do think its misused. The misusage of it is (sometimes) BS, for sure.
June 20, 2013
On Thursday, June 20, 2013 00:55:20 Szymon Gatner wrote:
> Anyway perfectly understandable POV. TDD is just a methodology. I myself not too long ago though it was *eghm* stupid to say the least. Why would I write the code entirely other way I was doing it until now? I am new to TDD but I must say I think it works for me. I am not ashamed to admit I am not to good in designing high level pieces of code. I know how to implement stuff but weeks/months later when requirements change I am always struggling with what i've done. Finally I see reusable pieces of code that I know I will be using in other possibly completely unrelated projects. Maybe TDD is just for stupid ppl like me? :P

I don't think that anyone's calling you stupid, but several of us do think that TDD is flawed. However, if it works for you, it works for you.

- Jonathan M Davis
June 20, 2013
On Thu, Jun 20, 2013 at 03:15:54AM +0200, Chris Cain wrote: [...]
> TDD (Test Driven Development) has a terrible name. Too many people think it's about the tests. Tests are certainly a good side effect of TDD and I certainly can't suggest they don't have value, but the fact that tests are written is really irrelevant to what you are _really_ trying to get out of TDD. There's been numerous attempts to rebrand it (Test Driven Design, Behavior Driven Development, and Behavior Driven Design to name a few).
> 
> Ultimately, the "goal" of TDD is to have the programmer sit down and look at code from a USER'S (as in, user of the API) point of view _FIRST_. "Writing a test" as the first step is really just suggesting "as a user, how would you find it convenient to use this thing?" That's really just about all there is to it. It's one of those things that helps guide your thought process. If you're already doing that without writing tests first, then great. I've always found it pretty helpful to play around with how I want code to look like before I actually code the implementation details, though. More often I can come up with a simpler and more robust design after playing with how I want the end result to look like. As a cool bonus, once everything is done, you'll have a "specification" showing all of the features of a class/package/module and how everything is used. It will also show what kinds of preconditions are necessary for using those things (for instance, does your class need particular services to be online for it to work? If so, it'll show up in the tests).
[...]

Thanks for your clarification! This is much more helpful than the definitions of TDD that I found online (e.g. on Wikipedia).

I will say, though, that this seems to be just Yet Another Buzzword for "API design". (I'm automatically skeptical when buzzwords are involved, because all too often, buzzwords are employed to make something rather ordinary appear to be special.) I agree that many APIs are poorly designed because the implementors were more concerned with the implementation, rather than how the users will be using it through the API.

OTOH, not all APIs should be designed to cater to the users' POV. I've encountered APIs that were *not* "convenient" in the way that I had preconceived, but in the end, it turned out that by forcing me to do things the "inconvenient" way, it not only opened up brand new ways of thinking about the problem, but also guided me into approaching the problem from an angle that is *efficient to implement*. It's just like Knuth said:

	"People who are more than casually interested in computers
	should have at least some idea of what the underlying hardware
	is like. Otherwise the programs they write will be pretty
	weird."

So I think a balance needs to be struck between what users would like it to be, vs. what is a good way of approaching the problem.  API's should not be so far removed from their implementations that the resulting code becomes "pretty weird", as Knuth puts it. TDD is good and all, but there's the danger of pushing it too far and applying it where it's not applicable.


T

-- 
Obviously, some things aren't very obvious.
June 20, 2013
On Wednesday, 19 June 2013 at 21:54:04 UTC, Walter Bright wrote:
> On 6/19/2013 4:01 AM, Szymon Gatner wrote:
>> This is not strictly D related but I am very curious about D's community opinion
>> on the points made by non other than Jim Coplien here:
>
>
> TDD strikes me as an ad-hoc way of constructing code, and is a poor substitute for thinking about the problem as a whole. For example, I don't really see how getting a square root function to pass its test cases is going to lead one to implementing one of the classic algorithms for computing square roots. I don't see how TDD for a compiler will lead one to rediscover what is known about the best way to organize the logic of a compiler.
>
> TDD is "curve fitting" which is what one does when one has no understanding.
I think their argument would be that if you're only interested in "classic algorithms" or "the best way" that is known, then you may not get much out of TDD.  But if you're exploring the solution space and trying to come up with a design that emerges with use, as opposed to thinking up a complete design in your head and then simply implementing it, I think they'd say TDD is a good solution for that.

Sometimes "ad-hoc" or "curve fitting" is a better way to begin, when you're entering a new domain, ie you don't even know what the curve looks like, say designing the first social network with 1 billion users. ;) At least that is what I understand of their method, I've never used it.