This article is exclusive to Pointer — a reading club for developers.
Signup at Pointer.io for our weekly newsletter.
Ten Good Rules for Bad APIs
We’ve all seen the presentations, meant to read the books, cursorily scanned the articles, and pretended to listen to respected and knowledgeable software architects talk on and on and on about how to create better APIs, what API design is, patterns for good APIs, blah blah blah blah blah. They just blather on at length, and about what? About methods? About classes? About actual coding?
No — they talk about proper abstraction. And conventions. And style. And layering. And modularity. And things that have nothing to do with how to cleverly name my exposed public fields or how many lines I can fit into a file before it surpasses the size limit of my IDE or how I can write the quickest code possible before I drop my awesome library out into the wide world and wait for eager to developers to jump onto it.
In that light, I thought I’d add some API rules of my own. Just as a counterpoint, like. Following are some of my personal favorites for creating APIs for the rest of us.
Rule #1: Write Similar Functionality in Multiple Ways
How many times have you tried to use an API and went looking for a method that did what you wanted, but you couldn’t find exactly what you were looking for?
This problem is exacerbated in modern development practice since nobody actually reads the API documentation. Instead, API discovery is through guessing and code-completion shortcuts. So if you’re trying to code-complete a method name that’s simply not there, you’re out of luck.
For example, maybe I wanted to add some things together. I’d assume that I’d find a method like this:
public float addSomeThingsTogether(float thing1, float thing2)
But maybe the library developer had created a method like this instead:
public float add(float thing1, float thing2)
In my IDE, I’d start typing “toge” because any reasonable method name would use the word “together,” and then I’d hit the shortcut for code-completion and the IDE would be of no help whatsoever. Because the API developer simply hadn’t thought about me and my important use case.
Now I could complain that their approach was too terse and vague and that they should have named the methods the way I wanted instead. But I can’t tell what was on their mind at the time, so I’ll withhold judgement and just put it down to inexperience, or a typing handicap, or an unfortunate case of unfettered stupidity.
But what I can complain about (and probably will, on Stack Overflow and Twitter) is that they didn’t offer reasonable alternatives. A good developer of bad APIs should anticipate the many, diverse ways that their API may be used and should write it to flexibly handle all of these situations.
For example, how hard would it have been for the developer to have offered the following set of methods instead of just that one?
public float add(…)
public float addTogether(…)
public float addThings(…)
public float addThingsTogether(…)
public float addSomeThings(…)
public float addSomeThingsTogether(…)
public float addTwoThings(…)
public float addTwoThingsTogether(…)
public float addTheseTwoThingsTogetherAndReturnTheResult(…)
With such a large set of options, I’d have a much better chance of finding the method I wanted.
Note that these methods represent a small subset of the possibilities and don’t capture the full API surface that is possible. Remember: adding additional API is just adding code, which only requires additional download time and bandwidth, plus extra storage on the device for every app that uses it. What’s the harm in taking up more resources for the end user if it might make the developer’s life a bit easier?
Rule #2: Write the Same Functionality in Many Ways
See? How hard was that? Rule #2 is justlike Rule #1, but slightly different.
Rule #3: A_CAPITAL_IDEA
Variety in capitalization exists in language for a reason. USE_IT!
Some programmers feel that CAPITAL LETTERS should only be used for specific types of API elements, like constant fields:
public static final int A_LOVELY_CONSTANT = 5;
But constant fields are not the reason that capital letters were invented, so why are we using them in this unnatural way?
Capital letters exist in our language for YELLING AT PEOPLE! And for EXPRESSING ENTHUSIASM! And for TELLING PEOPLE THAT YOU ARE NOT VERY FAMILIAR WITH COMPUTERS.
Use capital letters in your APIs whenever there is something REALLY IMPORTANT. Or when you are feeling particularly EXCITED! Or when you want your API to be used by computer-ignorant people who don’t know how to GET OUT OF CAPS LOCK MODE. For example:
public void DELETE_ALL_USER_DATA(boolean delete)
public void GO_SUPER_FAST()
public void HEY_DAD_THIS_METHOD_IS_FOR_YOU()
For example, see how much more descriptive this code is than it would be without all of the capitalization:
String s = makesomeboringdatabasequery(data);
Animation COOL_ANIMATION = CREATE_COOL_ANIMATION(data);
String s2 = getresponsefromuser();
Rule #4: USE_UNDERSCORES
This is related to Rule #3: if you’re going to use ALL CAPS, you’re going to need some underscore characters to separate all of those words. CamelCase must have been invented before capital letters, because it does a horrible job of SEPARATINGCAPITALIZEDWORDS. (See what I mean?) But_underscores_make_long_phrases_of_multiple_words_completely_readable.
For example, see if you can determine which pieces of code in the following snippet are more readable:
I can’t underscore this point enough. But_actually_I_can_and_you_can_totally_read_it_when_I_do.
Rule #5: Create Monolithic Methods
Everyone’s tired of writing a lot of code. Why not make it easier for your developers by allowing them to call fewer methods that do more?
Here’s an example: Suppose you have methods for basic math operations on some custom object:
public void multiply(SomeCustomObject object)
public void add(SomeCustomObject object)
public void subtract(SomeCustomObject object)
public void divide(SomeCustomObject object)
These seem sensible when you only need to do one of these operations. But what about when you have to combine multiple operations? Then you’d be forced to write a lot of code, like:
Ugh. How tedious.
Now look at this approach, which is more condensed and streamlined:
Now all you have to do as the API developer is to provide the matrix of all of the operations that you think your developers might need, like multiplysubtract(), and addmutiply(), and multiplydivide(). You probably also want to provide more complex variations, too, like multiplysubtractmultiply(), and addaddsubtractadddivide(). And of course you wouldn’t want to have to have your developers compose similar operations, so consider things like addaddaddadd() and dividesubtractdividesubtractdividedividedividesubtract() as well.
It’s this kind of careful forethought and up-front design that will help you create the most flexible API for your developers.
While these rules by no means encompass all techniques for creating bad APIs, they are at least a good start and should be adopted by any developer wishing to create the very worstAPIs.
Oh, and did anyone notice that the title “Ten Rules…” doesn’t quite match the content, since I only got as far as Rule #5? This mistake was intentional, and provides a nice segue for my final rule:
Rule #6: Always Ship an Incomplete API
You know that other requirements will come up, and there’s always another version you can ship. So why bother finishing what you have? Just ship it. Mistakes and omissions: they’re why version numbers were invented.