"Here's a binary search algo description, implement it perfectly without testing".
That's not even close to how developers work, and he amplifies the disconnect with the real world by choosing a problem known for it's boundary and off-by-one risks.
The code will behave incorrectly on some edge cases, but if the developers and QA have covered the problem domain pretty well, it won't matter. First, most of the edge cases will never happen. Second, people get angry when software fails for no obvious reason, but they tend to be a lot more forgiving when software fails on inputs that are "freakish" or "wrong" in the domain context. Searching the widget catalog results in "500 Internal server error" when the widget catalog is empty? Well, duh! When should the widget catalog ever be empty?! The search page shows screwy results because someone entered an item with a negative price? Well, duh! Get that stupid item out of the catalog! Instead of thinking like a mathematician or a system designer in order to avoid mistakes, the typical development approach is to think like the customer so you only make mistakes that the customer can sympathize with.
I would also point out that for a lot of programming tasks, particularly in a corporate IT environment, "thinking like the customer" can be a good thing: it helps you make sure that you are building something that truly helps the customer do their job.
The risk is that no matter how much "domain experience" you have, the customer has more, and you can fool yourself into thinking that you understand what they need better than they do.
Also: no amount of test-drivenness can save you from just not grokking the problem (as Peter Norvig's Sudoku solver showed ... er, by counter-example.)
The fact that it's tractable to rigorously consider all the edge cases means it is ridiculously simple compared to common real-world programming tasks.
You'd be surprised. Also, terrified.
So, one of the assignments was to write a program, and desk check it until you were confident that it would compile and run correctly on the first try. It was a one-chance, pass/fail assignment: it either worked, or there was a compile or runtime error.
Maybe in the "real world", the programming is just the simple gluing together of hard stuff like databases, compilers and operating system, since they have already been written. Once you have some bullet-proof method, it really is better to just be a consumer of it.
There is still some hard stuff that needs to be done, though. I suspect that to do "the hard stuff" you need to be able to implement simple-but-serious algorithms in a few lines. This makes your overall code smaller, more readable and allows optimization for the particular cases you're working with.
I don't think the parent was objecting to implementing a simple but serious algorithm in a few lines, but to doing so without performing any testing at all.
There is still some hard stuff that needs to be done, though.
Yes, and I'm pretty sure the people doing that hard stuff do some testing ;)
raganwald says, "It feels very retrograde to try to write the whole thing out and only then start thinking about [...] edge cases." Nobody said you had to write it before thinking about edge cases! The idea is to think about the edge cases and incorporate that thinking into the code before you actually test the code--whether you can be--actually whether you know the steps to be--that thorough in your head.
One way to think about edge cases, of course, is to write the test code before the function. But after writing the tests, try writing the function correctly before testing it.
Of course, one of the potential bugs in binary search doesn't occur to people before they write and then try it. And, that bug is hard to test for, ahem, don't want to explain why.
However, not all code is developed incrementally, testing miniscule details on the way. Some code is written perhaps 20 lines at a time before testing. Binary search is only about 20 lines, my implementation is significantly less.
So there are cases where 20 lines are written, then tested. The thought experiment here is to assume that this is one of those cases, one instance of which there are probably 5 or 10 in a moderate sized project. Places where people perhaps got a little lazy, it was obviously easy, or didn't bother following the full TDD.
And as I say in a comment elsewhere, only 1 in 3 of the people who tested passed my test suite.
Your point is valid, but misses some of the point of the article, and the exercise.
These types of problems are poster children for TDD and testing. There is under 20 lines of code but several ways to make logical errors. If we test drive the code, we can ensure that the algorithm performs exactly how we want it to.
(1) Don't ask me how I know :)
In fact, my 'attitude' of approaching work like a craftsman has served me very well in job interviews (even landing me opportunities at pie-in-the-sky type of jobs).
It's an idea I explored very poorly in an old blog post:
http://github.com/raganwald/homoiconic/blob/master/2009-06-0...
I dunno - for something as simple as a binary search there is nothing to increment. Maybe I misunderstand and you meant "incremental" as in where you write a whole algorithm, make sure it works, and then improve it (optimise/remove bugs/add features - not finish it)?
"It feels very retrograde to try to write the whole thing out and only then start thinking about off-by-one errors, computer arithmetic issues, and other edge cases."
Yeah... that is backwards. You should think of these things in advance, and if you have experience, you might have had to deal with them already. Binary search is especially simple - if you get the algorithm wrong I'm inclined to believe you are inexperienced, but if you just make typos and such, well... I'd expect that from anyone.
Then again all the rapid application development options today mean that anyone can use a binary search without writing one... or even write complete software with no real understanding of how it works.
Maybe I'm just out of date?
I strongly suspect--and I am not being sarcastic or falsely modest--that I am not up to scratch as far as programming is concerned. I almost always get stuff like this wrong when I implement it. My code breaks for empty lists, or with just one element, or lists with an odd number of elements, or something else I can't imagine.
Exposure to recursive algorithms has taught me to build this kind of thing up from simple cases like an empty list or a list with one element. So yes, I really would write a version that works for the empty list, a version that works for either the empty list or just one element, and go from there.