I always tried to make the whole program better and that would often mean that even though there was one little problem, I would try to understand the whole program. I would try to find the problem by reasoning down from the top and finding it as opposed to saying, “Oh, this isn’t working. Do surgery here.” So there were some things where I just took too long and spent too much time trying to import the whole thing into my head, when a more directed approach just to go fix it would do.
But usually when Steve left a project, it was hard to revise the code to make it do stuff. Whereas I tried to keep things good, but it meant that if a program was really big and awful, I would spend a lot of time spinning my wheels before I felt comfortable diving in. But that didn’t happen very often because often when I debug things, I don’t do it by debugging.
As I mentioned before, there were many bugs that I never had any clue where they were. I just get to a point where I say, “This piece of code is supposed to be doing this. This does not look like it’s doing that. I mean, how could anybody have written this complicated bit of code to do this simple thing?” So I’d rip it out and replace it with a routine that does the simple thing I thought that piece of code was supposed to do. And the program magically works. In retrospect, what had happened is that the program had evolved and this little routine kept getting changed. Rather than being replaced when the program evolved, somebody was patching it to do different things and missed once.
I never debugged any of that stuff. I hack on it for a day or two doing all of this typing and nobody would have a clue of what I’m doing and the program would get fixed. What a debugger! That is very dangerous because Will’s dictum is basically right, that if you rewrite a hundred lines of code, you may well have fixed the one bug and introduced six new ones. And at least the one bug you, knew what to look for; you now have to start looking for the six new ones. And I was just fortunate because I had a very good track record over the years of managing to write code that, for the most part, worked.
Seibel: So you must have had some strategies for reading code. Even if there’s no bug but just a big pile of code that you’re going to work on, how did you tackle that?
Cosell: Not very well, it turns out. One of the reasons why I tend to rewrite chunks of code rather than fix them is because I reach points where I can’t manage to figure it out anymore. I don’t read the code as if it were a book. I try to figure out what the program is doing and then get hints about the code from the top down.
In parallel with reading the program I think about how would I solve this problem. Which means I’m looking for certain specific pieces so I can say, “Oh, here’s where the program does it.” Then I can say, in my usual arrogant way, that the guy that wrote this did it wrong. Or at least I now understand that they’re doing this some other way.
So I would go top-down. But some of the guys I knew were spectacularly good at bottom-up. They would start reading little subroutines and eventually find the one subroutine they needed. But mostly for those kinds of things I was a top-down kind of guy. That is, I’m looking at the program trying to figure out what the other programmers should have done. That was one of the things that led me to sometimes fix bugs where I didn’t know what the bug was. I’d hit a place where I say, “This piece of code—as I understand this program now—is supposed to be doing this” and then either the code I’m looking at doesn’t do that or the code is so complicated and seems to be doing six other things and it’s not making sense to me.
In either case my usual response at that point is to fix that piece of code so that it agrees with what I thought was supposed to be happening there in the program. You can see how fabulously dangerous that can be because there is no one correct way to organize a program and if the program was perfectly well organized but in a different way than I wanted, I have now just killed the program and now have an incredible avalanche of stuff to fix. But I was pretty lucky with that. Usually when I said, “This looks wrong and I’m going to fix it” it got fixed. And that was even true from the early days.
The first big program I worked on, the PDP-1 time-sharing system, I was just a raw programmer doing college-undergrad programming problems and I moved through the hospital project very quickly, from doing applications to coming under the wing of the systems guys. Even though I was six months into being a professional programmer I was perfectly willing to say that this little piece of the remote process swapper doesn’t look like it’s right and I would rewrite it.
Seibel: In addition to the danger of introducing new bugs, another risk is that you may have misapprehended what the program is supposed to do.
Cosell: That’s right. The path I took was, if you will, not for the faint at heart. At the time I was 19 years old and that seemed like the only way to do things. I had two convictions, which actually served me well: that programs ought to make sense and there are very, very few inherently hard problems. Anything that looks really hard or tricky is probably more the product of the programmer not fully understanding what they needed to do and pounding it with a hammer ’til they got code that looked like it did the right thing.
I don’t know why I had those two convictions. I arrived at BBN with no skill per se, but I had those principles in the back of my head for some reason. I thought I ought to be able to understand anything and it shouldn’t be so hard. I found that even for the time-sharing system and the IMPs—for all of those class of programs, that proved to be true. In general once I had the right understanding of what a program was supposed to do, the pieces would fall into place. The pieces that didn’t belong would stand out like a miscolored piece in a jigsaw puzzle.
Another principle was I always wanted clean listings. I wanted the thing to be just right. When you have to fix a bug in a program you never, ever fix the bug in the place where you find it. My rule is, “If you knew then what you know now about the fact that this piece of code is broken, how would you have organized this piece of the routine?” What were you thinking about wrong before? Fix the code so that can’t happen. When you finish with a routine I want every routine you work on to look as if it was just written. I do not want to see any evidence of afterthoughts or things gone wrong followed by something to correct the error or a mysterious piece of code saying, “This routine returns the wrong value every now and then so I’ve got to fix it.” I don’t want to see any of that. I want to see code that looks like through some divine inspiration you got it exactly right the first time.
Then I compound that with one other little trick. I got this when I was working on D.O.D. projects. They’ll never fund a new project. Both BBN and the government have too much invested in the current program, even when it has limitations that are awful that need to be fixed. The most common one is something that you did that was right when the program started is now hopelessly wrong because the program’s use or requirements or something have evolved. What you’d like to do is rip out that part of the program and just fix it. And they say, “What is that going to improve?” You say, “It’s not going to improve anything but it’ll make the program better for next week.” Not going to get permission to do that.
The method I took is the sneaky way and this has worked very well for me for a lot of programs. I do a design of the future version of the program. Knowing what I know now, this is how the program would have looked, now at the program level rather than at a subroutine level. Now when you go to fix a bug and you have a choice on how to fix it, fix it moving toward the better model. Don’t just fix it in the shortest way. Don’t just fix it in the way that fits, but move it toward the other model so that over several months instead of the program getting more and more mired in patches fixing up the stuff that was old and wrong, all the critical parts of the program all of a sudden look like they’re the new way of doing things. Often you can get to the point where there are so few places left that still do things the old way that you can slip in and get those fixed because you’re now not damaging the whole program.