|laziness, impatience, and hubris|
The thing I was thinking of specifically was the program linogram that I presented in Chapter 9 of my book. When I first started the project, I had only the dimmest notion of what I wanted the program to do. I knew that the user would describe a diagram with a text file, and include some equations relating parts of the diagram to each other, and that the program would solve the equations and then draw the diagram. So there was a long period during which I really wasn't sure what the program would do.
Then there was another, longer period during which I thought I knew what I wanted it to do, but I had no idea how the program was going to do it. Where does it get the equations from? Aren't there implicit equations? What is the input format? And during this phase there was constant interplay between what I thought I could program and what I wanted the program to do. What information can the program infer, and what must the user give it explicitly? Will the input be ambiguous? Should the program try to disambiguate it? What kinds of figures wil the user want to draw? Boxes and lines and arrows, sure; what about other things? But to calculate the position of the arrowhead on an arrow, you need to solve trigonometric equations, which are really hard. So do you forbid arrows? Or do you find some way to restrict the trigonometry to be possible? Or do you go whole hog and put a complete computer algebra system into the program? Or do you use a clever trick? And if so, what is the clever trick?
So through all this, and I think it took three or four months, I had no thought of writing tests, and I'm not sure what it would even mean to write a test.
Then these was a phase during which I started implementing stuff, and for every function I wrote that ended up in the final product, I probably threw away four or five others that turned out to be impossible, or that exposed some confusion in my mind about how such a program should really work, or that ended up sending me back to stages 1 and 2 to redesign enough of the whole thing that the function no longer made sense. I suppose I could have written tests for some of this stuff, but it would have been a complete waste of time. I had bigger fish to fry.
Writing tests is great when you know what the program is supposed to do. But it seems to me that if you know what the program is supposed to do, you already have the really hard part of the problem solved. Development and maintenance programmers forget this because their bosses and clients hand them descriptions of what the programs are supposed to do.
After I had the basic system running, and the book was published, I undertook a major revision, which I hope to release this month. The first thing I did to start the revision was to write test apparatus. The test suite has grown rapidly and has been a big help. But I think trying to write tests before the first version was complete, when it was still a pulsating, inchoate mass of murky thought-stuff, would have been a waste of time.
The thing that really struck me after the testing session was that the people who came up to talk to me did not seem to understand what I was talking about. They really didn't seem to get just how vague my idea was and for how long. They kept thinking of programs where some little tiny thing was unspecified, but I was talking about projects where you really have no idea at all what you want, or what is possible, or how those two things relate to each other.