My conference session How to Design a Good API and Why it Matters has always drawn large crowds; on InfoQ was the third most viewed content last year. When I presented this session as an invited talk at OOPSLA 2006, I was given the opportunity to write an abstract for the proceedings. In place of an ordinary abstract I decided to try something a bit unusual: I distilled the essence of the talk down to a modest collection of pithy maxims, in the spirit of Jon Bentley's classic Bumper-Sticker Computer Science, Item 6 in his excellent book, More Programming Pearls: Confessions of a Coder (Addison-Wesley, 1988).
It is my hope that these maxims provide a concise summary of the key points of API design, in easily digestible form:
All programmers are API designers. Good programs are modular, and intermodular boundaries define APIs. Good modules get reused.
APIs can be among your greatest assets or liabilities. Good APIs create long-term customers; bad ones create long-term support nightmares.
Public APIs, like diamonds, are forever. You have one chance to get it right so give it your best.
APIs should be easy to use and hard to misuse. It should be easy to do simple things; possible to do complex things; and impossible, or at least difficult, to do wrong things.
APIs should be self-documenting: It should rarely require documentation to read code written to a good API. In fact, it should rarely require documentation to write it.
When designing an API, first gather requirements—with a healthy degree of skepticism. People often provide solutions; it's your job to ferret out the underlying problems and find the best solutions.
Structure requirements as use-cases: they are the yardstick against which you'll measure your API.
Early drafts of APIs should be short, typically one page with class and method signatures and one-line descriptions. This makes it easy to restructure the API when you don't get it right the first time.
Code the use-cases against your API before you implement it, even before you specify it properly. This will save you from implementing, or even specifying, a fundamentally broken API.
Maintain the code for uses-cases as the API evolves. Not only will this protect you from rude surprises, but the resulting code will become the examples for the API, the basis for tutorials and tests.
Example code should be exemplary. If an API is used widely, its examples will be the archetypes for thousands of programs. Any mistakes will come back to haunt you a thousand fold.
You can't please everyone so aim to displease everyone equally. Most APIs are overconstrained.
Expect API-design mistakes due to failures of imagination. You can't reasonably hope to imagine everything that everyone will do with an API, or how it will interact with every other part of a system.
API design is not a solitary activity. Show your design to as many people as you can, and take their feedback seriously. Possibilities that elude your imagination may be clear to others.
Avoid fixed limits on input sizes. They limit usefulness and hasten obsolescence.
Names matter. Strive for intelligibility, consistency, and symmetry. Every API is a little language, and people must learn to read and write it. If you get an API right, code will read like prose.
If it's hard to find good names, go back to the drawing board. Don't be afraid to split or merge an API, or embed it in a more general setting. If names start falling into place, you're on the right track.
When in doubt, leave it out. If there is a fundamental theorem of API design, this is it. It applies equally to functionality, classes, methods, and parameters. Every facet of an API should be as small as possible, but no smaller. You can always add things later, but you can't take them away. Minimizing conceptual weight is more important than class- or method-count.
Keep APIs free of implementations details. They confuse users and inhibit the flexibility to evolve. It isn't always obvious what's an implementation detail: Be wary of overspecification.
Minimize mutability. Immutable objects are simple, thread-safe, and freely sharable.
Documentation matters. No matter how good an API, it won't get used without good documentation. Document every exported API element: every class, method, field, and parameter.
Consider the performance consequences of API design decisions, but don't warp an API to achieve performance gains. Luckily, good APIs typically lend themselves to fast implementations.
When in Rome, do as the Romans do. APIs must coexist peacefully with the platform, so do what is customary. It is almost always wrong to “transliterate†an API from one platform to another.
Minimize accessibility; when in doubt, make it private. This simplifies APIs and reduces coupling.
Subclass only if you can say with a straight face that every instance of the subclass is an instance of the superclass. Exposed classes should never subclass just to reuse implementation code.
Design and document for inheritance or else prohibit it. This documentation takes the form of selfuse patterns: how methods in a class use one another. Without it, safe subclassing is impossible.
Don't make the client do anything the library could do. Violating this rule leads to boilerplate code in the client, which is annoying and error-prone.
Obey the principle of least astonishment. Every method should do the least surprising thing it could, given its name. If a method doesn't do what users think it will, bugs will result.
Fail fast. The sooner you report a bug, the less damage it will do. Compile-time is best. If you must fail at run-time, do it as soon as possible.
Provide programmatic access to all data available in string form. Otherwise, programmers will be forced to parse strings, which is painful. Worse, the string forms will turn into de facto APIs.
Overload with care. If the behaviors of two methods differ, it's better to give them different names.
Use the right data type for the job. For example, don't use string if there is a more appropriate type.
Use consistent parameter ordering across methods. Otherwise, programmers will get it backwards.
Avoid long parameter lists, especially those with multiple consecutive parameters of the same type.
Avoid return values that demand exceptional processing. Clients will forget to write the specialcase code, leading to bugs. For example, return zero-length arrays or collections rather than nulls.
Throw exceptions only to indicate exceptional conditions. Otherwise, clients will be forced to use exceptions for normal flow control, leading to programs that are hard to read, buggy, or slow.
Throw unchecked exceptions unless clients can realistically recover from the failure.
API design is an art, not a science. Strive for beauty, and trust your gut. Do not adhere slavishly to the above heuristics, but violate them only infrequently and with good reason.
Watch Presentation: How to Design a Good API & Why it Matters
Joshua Bloch is Chief Java Architect at Google, author of Effective Java, Second Edition (Addison-Wesley, 2008), and coauthor of Java Puzzlers: Traps, Pitfalls, and Corner Cases (Addison-Wesley, 2005) and Java Concurrency in Practice. He was a Distinguished Engineer at Sun Microsystems, where he led the design and implementation of numerous Java platform features including JDK 5.0 language enhancements and the Java Collections Framework. He holds a Ph.D. from Carnegie-Mellon and a B.S from Columbia.