Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

At what point do you generalize to doing arithmetic and what makes that point better then any point beforehand? What is it that you learned from (4,6) and (10,2) that you didn't know before?


There starts to be duplication in the implementation that you can eliminate by refactoring.


Refactoring is not (necessarily) generalization. I am interested in how you decide you have seen enough examples to generalize (to actually doing arithmetic in this case) and what makes you trust your judgment at that point while rejecting your judgment initially.


Of course, arithmetic is a somewhat silly example, because we generally tend to have arithmetic built in, and we all know how arithmetic works.

"Refactor to remove duplication" is absolutely a standard step of TDD. How you do it is up to you. This is not an automated process, it requires (design) skill and good judgement just like any other programming.

I recall once seeing Beck look at two loops that were quite dissimilar: they had different for structures, and different contents, which is pretty much nothing duplicated except the word "for", and the fact that they were looping - differently - over the same collection. He changed the second loop to loop the same way the first one did. This required changing the body of the loop to skip over the items toward the end of the collection, since the previous version only did the front of the collection.

Now the for statements were the same. "Well, gotta eliminate that duplication, he said, and moved the second body into the first loop and deleted the second loop entirely. Now he had two kinds of similar processing going on in the one loop. He found some kind of duplication in there, extracted a method, did a couple of other things, and voila! the code was much better.

http://wiki.c2.com/?OnceAndOnlyOnce

What this approach does is move most of this type of tricky design work to a phase of development when you are not also trying to figure out what the program is trying to do or how to do it. Instead, while you are doing this design work, the behaviour of the program remains fixed and you have tests to make sure that's actually the case.

Having the code remain functionally identical (in terms of the tests) is a lovely safety net to have while you are doing fancy coding acrobatics. If you find you're in a dead-end, revert, check that the tests are still green and try again, with the knowledge you've just gained.

I guess this is something that older developers are more attuned to, because they have been humbled by code enough to appreciate the limitations of their cognitive faculties.

> rejecting your judgment

I trust my judgement, always, and am wary of my judgement, always. But I also listen to the code, and I just don't "remove duplication" until there actually is duplication.

http://wiki.c2.com/?YouArentGonnaNeedIt

https://en.wikipedia.org/wiki/Rule_of_three_(computer_progra...

https://blog.codinghorror.com/rule-of-three/


Thanks for the time you took to reply. Just to get something quickly out of the way, yes - it is a silly example but this is the one you chose or at least chose to defend. I will be interested to see an an equivalent of "return 9" in an example that is not silly.

While you make good points about the value of refactoring and having a well tested code, they are not specific to TDD. Nor do they address my question. It seems to be a fundamental property of TDD that a developer is invited to reduce her mental capacity - up to a point, "return 9" or as you suggested "if ((a == 10) && (b == 2)) return 12". Then something unexplained happens e.g.: "refactor the add method to handle all those cases with less code, by removing the case-handling and doing arithmetic.".

What exactly happened here? Why is the developer can now use that big brain of hers 100%, realize that special case handling is not the way to go, and write "a + b"?


There is no switch between "brain on" and "brain off", what you describe as "a fundamental property of TDD" to "reduce mental capacity" is just a misunderstanding on your part.

> Then something unexplained happens

I am not sure why you think this is "unexplained", as it is spelled out in detail in every TDD introductory text.

What there is is a switch between (a) trying to get the tests to pass (tests are red) and (b) trying to refactor the code (tests are green). In both cases, you use your full brain, in case (a) you use it to find the minimal code to pass the tests. If you think finding minimal code is a "brain off" activity, well that's a whole different discussion, but I refer you to Saint-Exupéry[1] and Blaise Pascal[2], for starters.

And in each case you use code as forcing functions for other code. The tests force you to write the production code. Writing only the least possible production code that passes the tests forces the tests to actually test what you want them to test. If you write code that does significantly more than what your test requires, you won't have test coverage for that excess capability. And so if the rest of your program exercises that excess functionality and you break it, your program will break without your tests giving you a warning.

Also, the silliness of the example is of course what makes it a good illustrative example. If we had to first become domain experts with s shared understanding of some random domain, it would make talking about TDD itself much harder. It does require the ability and willingness to use it as an illustrative example.

[1] https://www.goodreads.com/quotes/19905-perfection-is-achieve...

[2] https://quoteinvestigator.com/2012/04/28/shorter-letter/


As before, I'll ignore appeal to authorities :-).

I'll try to rephrase my doubt about TDD. It seems to be the case that TDD proponents claim that

A. Developers not following TDD tend to produce poorly testable code that may be unnecessarily general i.e. attempting to solve problems that need not be solved given the problem.

B. TDD solves the problems by setting a prescriptive template (red -> green -> refactor)

The "refactor" part of B includes syntactic manipulations (e.g: renaming variables, merging if statements,...) which requires little creativity or insight and generalization which usually do require creativity and insight. My question is this: If you accept A how can you trust the developer to correctly "generalize" in B ?

I am really interested in an non trivial example that benefits from "return 9" testing. How about the following problem that does not require one to be a domain expert: implement the function: areCoPrime( a : Int , int b : Int) : Boolean that returns true IFF a and b are co-prime. What examples will you test for and how and at what point would you generalize?


> appeal to authorities :

There are no appeals to authority here, just references to more information. You know, this "web" thing, with links and stuff. I don't think it'll ever take off...

> rephrase my doubt about TDD

Hmmm...so you no longer hold on to your previous claim of TDD being about selectively turning the brain off?

> TDD proponents claim that

> A. Developers not following TDD tend to produce poorly testable code that may be unnecessarily general

No. Pretty much all developers have a tendency to do that:

   http://www.lmgtfy.com/?q=%22Premature+abstraction+is+the+root+of+all+evil%22
> B. TDD solves the problems by setting a prescriptive template (red -> green -> refactor)

No. First, no mechanical prescription can solve this problem. Second, if anything the "solution" is to follow YAGNI[1] and DTSTTCPW[2]. As I have explained before and you continue to ignore, where TDD helps is by splitting the task into two, or better, three phases

1. Write a test

You are asked to produce a specific test case, not really that much of a chance of overgeneralising (though not zero chance, see parallel thread).

2. Write minimal code to make the test pass

Since you are only asked to make the test pass, and the goal is to do this as "stupidly" as possible, chances of overgeneralising are again minimised.

3. After the test passes, refactor to remove duplication

At this point, you are only trying to remove duplication, you are no longer trying to solve the original problem, so your scope is much more constrained.

Is this some sort of mechanical or mathematical guarantee that you cannot overgeneralise? Hell no! As I have consistently maintained, TDD is not a "brain off" technique, though you seem to insist on introducing that straw man, only to then object to it. However, it helps tremendously, by drastically reducing the opportunities for overgeneralising (one phase out of three), and even there drastically reducing the incentive and scope for overgeneralising, because you are focused on something very concrete.

[1] https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it

[2] http://c2.com/xp/DoTheSimplestThingThatCouldPossiblyWork.htm...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: