It doesn’t matter how good you are; you can’t get an API right until you’vetried to code to it. You design something; try to use it; and say, ”Oh, this isso wrong.” And if you do this before you’ve wasted time writing all of thelayers underneath it, that’s a huge win. So what I’m talking about is test-firstprogramming and refactoring the APIs, rather than refactoring theimplementation code underneath the APIs.

As far as doing the simplest thing that will work, I’m all for it. Thefundamental theorem of API design is, when in doubt, leave it out. It should bethe simplest thing that is big enough to handle all the use cases that you careabout. That doesn’t mean “Just throw some sloppy code together.” Thereare oodles of aphorisms to this effect. My favorite is one that’s commonlymisattributed to Thelonious Monk: “Simple ain’t easy.”

Nobody likes sloppy software. People who say, “Write the simplest thingthat could possibly work and refactor mercilessly” aren’t saying, “Writesloppy code,” and they aren’t saying, “Don’t do upfront design work.” I’vetalked to Martin Fowler about this. He’s a huge believer in thinking aboutwhat you’re going to do so your system has a reasonable shape and areasonable structure. What he’s saying is, “Don’t write 247-page specsbefore writing a line of code,” and I agree.

I do disagree with Martin on one point: I don’t think tests are even remotelyan acceptable substitute for documentation. Once you’re trying to writesomething that other people can code to, you need precise specs, and thetests should test that the code conforms to those specs.

So there are some points of disagreement between the two camps, but Idon’t think the gulf is as wide as some people do.

Seibel: Since you mentioned Fowler, who’s written a couple of books onUML, do you ever use UML as a design tool?

Bloch: No. I think it’s nice to be able to make diagrams that other peoplecan understand. But honestly I can’t even remember which components aresupposed to be round or square.

Seibel: Have you ever done full-on literate programming a la Knuth?

Bloch: No. I’m not against it in principle. I just haven’t had occasion to doit. The other thing is—how can I put this delicately—I tend not to buy intoreligions, any religions, whole hog. Whether it’s object-orientedprogramming or functional programming or Christianity or Judaism—I minethem for good ideas but I don’t practice them in toto. There are a lot ofgreat ideas in literate programming, but it’s not the right bar: there aren’tenough other programmers hanging out there. I could see maybe doing itonce as an experiment.

What I do instead is I will cheerfully spend literally hours on identifiernames: variable names, method names, and so forth, to make my codereadable. If you read some expression using these identifiers and it readslike an English sentence, your program is much more likely to be correct,and much easier to maintain. I think that people who say, “Oh, it’s notworth the time; it’s just the name of a variable,” just don’t get it. You’re notgoing to produce a maintainable program with that attitude.

Seibel: One way that programs differ from most literature—nonexperimentalliterature anyway—is that there is no one order in which toread a program. How do you read a big program that you didn’t write?

Bloch: Good question. The truth is I really want programs to be wellwritten.I know a few people with the ability to take an arbitrarily large andpoorly written system and wrap themselves in the code till they get a totalmental picture of the architecture. It’s a really useful skill, but I’ve neverbeen able to do it.

I want to be able to take small modules, read them, and understand them inisolation. If I’m trying to read a system that’s tightly coupled so I have toread the whole thing in order to understand one part, it’s a nightmare. Ihave to psych myself up even to attempt to read it, and I have to have accessto all the code at the same time. I usually print everything out and sit on thefloor surrounded by the printout, writing notes on it.

If I’m reading a well-written piece of code, I try to find a view from 10,000feet: usually someone, somewhere has written a description of the shape ofthe entire system. If I can find it, I know what the important modules are,and I read them first, occasionally diving down into lower-level modules toaid my understanding.

Also, although code is written linearly down the page, the execution is notat all linear. If I’m lucky enough to have a piece of code that can be readfrom top to bottom, great. If not, it’s important that I have access to toolsthat let me quickly locate methods that are being invoked, classes that arebeing extended, and so on. This lets me understand key execution pathsthrough the code.

Seibel: Do you ever step through code as a way of understanding it?

Bloch: Absolutely! That is still my chosen method of debugging. Especiallyfor concurrent code—there are too many states that the thing can be in forme to possibly enumerate all of them. I just stare at the code; step throughit mentally; think of what invariants must hold at what time. For all of thefancy debugging tools at our disposal, there’s nothing that can match thepower of simply stepping through a program, in a debugger or by reading itand mentally executing the code. I’ve found many bugs that way and I use itas part of the writing process.

As I write the program, I say to myself, what it is that must be true here?And it’s very important to put those assertions into the code, to preservethem for posterity. If your language lets you do it with an assert construct,use it; if not, put assertions in comments. Either way, the information is toovaluable to lose. It’s what you need to understand the program six monthsdown the road, and what your colleague needs to understand the programany time at all.

Seibel: Do you feel like people understand invariants and how to useassertions as well as they ought?

Bloch: No. You probably know that assertions were the first constructthat I added to the Java programming language and I’m well aware that theynever really became part of the culture. Only a small fraction of Javaprogrammers use them. I don’t exactly know why that is. Talking ofmathematics—invariants are very much a mathematical idea.

Seibel: But you don’t have to have a lot of math to be able to understandit.

Bloch: You don’t. But let me just play the devil’s advocate. There’s acertain precision of thinking that comes with doing math. I coached a MathOlympiad team for fourth and fifth graders. This is just the age at which somekids are starting to understand, at some level, the notion of a proof—that aproposition can be demonstrably, unequivocally true rather than just, “I thinkit’s true because here are a few examples where it seems to work.” In order tounderstand the notion of an invariant, you have to understand the notion of aproof. Unfortunately, there are plenty of adults who don’t. And it’s a styleof thinking that is typically taught in mathematics classes.

Seibel: You’d almost wonder if maybe the better forum to teach that kindof thinking would be in programming. If you just taught programming asbeing about invariants—

Bloch: To a certain extent I agree, but you can go too far in that direction.Then we’re back to Dijkstra. I’m sure you’ve read “On the Cruelty of ReallyTeaching Computing Science”, which I think is as wrong as it could possiblybe. Dijkstra says that you shouldn’t let students even touch a computer untilthey’ve manipulated symbols, stripped of their true meaning, for a semester.That’s crazy! There’s a joy in telling the computer to do something, andwatching it do it. I would not deprive students of that joy. And furthermore,I wouldn’t assume that I could—computers are everywhere. Ten-year-oldsare programming.


Перейти на страницу:
Изменить размер шрифта: