Joshua Bloch
179
use cases, you build the thing and then you try to do something very simple
and you realize that, “Oh my gosh, doing something very simple like taking
an XML document and printing it requires pages upon pages of boilerplate
code.” And that’s a horrible thing.
So get those use cases and then write a skeletal API. It should be really,
really short. The whole thing should, usually, fit on a page. It doesn’t have to
be terribly precise. You want declarations for the packages, classes, and
methods and, if it’s not clear what they should do, then maybe a one-
sentence description for each. But this is not documentation of the quality
that you will end up distributing.
The whole idea is to stay agile at this stage, to flesh the API out just enough
that you can take the use cases and code them up with this nascent API to
see if it it’s up to the task. It’s just amazing, there are so many things that
are obvious in hindsight but when you’re designing the API, even with the
use cases in mind, you get them wrong. Then when you try to code up the
use cases you say, “Oh, yeah, this is fundamentally wrong; I have too many
classes here; these should be combined, these need to be broken out,”
whatever it is. Luckily, your API doc is only a page long, so it’s easy to fix it.
As your confidence in the API increases, then you flesh it out. But the
fundamental rule is, write the code that uses the API before you write the
code that implements it. Because otherwise you may be wasting your time
writing implementation code that won’t get used. In fact, write the code
that uses the API before you even flesh out the spec, because otherwise you
may be wasting your time writing detailed specs for something that’s
fundamentally broken. That’s how I go about designing stuff.
Seibel: And how specific is this to designing things like the Java collections,
which are a particular kind of self-contained API?
Bloch: I claim it’s less specific than you might think. Programming of any
complexity requires API design because big programs have to be modular,
and you have to design the intermodular interfaces.
Good programmers think in terms of pieces that make sense in isolation,
for several reasons. One is that you, perhaps inadvertently, end up
producing useful, reusable modules. If you write a monolithic system and,