People disagree what a good API looks like. However, I think everybody can agree not this.

I had this experience working on transforming a website to a mobile app. The main company developed the website in-house, while the app was outsourced to me. However, the developers seemed to keep forgetting that a good number of their users depended on the API being sort of stable…

“The documentation was out of date so we took it down”

One of the first things you usually do when looking at an API is check the documentation. Luckily, I quickly found a link… that timed out. I asked them about it, and they said that the documentation was out of date so they took it down. I politely asked them how I was supposed to update the app without documentation.

Let every method get passed an array of parameters

Soon, the documentation was back up, though it was still unclear if it was recompiled or still out of date. I was hopeful. There was a clear list of categories, each with methods. The documentation seemed to be automatically generated, which gave me hope that it was up to date.

Everything changes when I actually checked the documentation for a method. The method took one parameter. An array called “args”. Luckily, some, but not all, methods had examples.

Have documentation consisting only of examples

Some methods luckly also have descriptions. Unfortunately, I soon find out the description is just one example. Because you are a super human, you might find it easy to understand the meaning of createNewUser(['bill', 4, 25, 522, 'Bill', false, false]) I however, had some trouble.

If something was said about the return value, it was equally opaque. Everything returned “dynamic” according to the generated documentation, some had examples, but these examples where often incomplete.

Have the same API call for different types of data

One of the method was something like:

getUserGroup(groupId)

/* Returns:

-> {"name": "group0",

"type": "none",

"id": "45",

"group": 45,

"users": [

{"name": "...", "type": ""}

]} */

This worked well enough. However, strangely every group contained one “ghost user” with the type set to none and the same name as the group. Filtering these out for display was easy enough, and I thought no more about it.

Later, the client requested a feature to be able to create groups. However, there seemed to be no createGroup API call. After contacting the developers, who replied several days later, it turned out the way to create a group is using createUser with type="none" . The “ghost user” was actually the group itself which was not really a group but a special kind of user. Members where just users that shared a group ID, while the group ID was not the same as the ID of the group. Obviously, none of this was documented anywhere.

Keep making breaking changes without showing any sign

Good APIs use a thing called versioning, so that old applications can still access old versions of the API.

Average website use a kind of versioning, mostly consisting of errorring out if the version numbers don’t match up.

However, marvelous APIs do neither. The version number, if even available, is always 1.0, no matter what changes. Argument order may change, methods may dispensary, etc. Not only will you break old applications, old applications will have no way of knowing that they are broken.

Changing argument order is especially fun. API calls may seem so succeed, but do something drastically different than the application expects. Suppose initially your have an API method like this:

shareDocument(document, user, allow_view=True, allow_edit=False, transfer_ownership=False)

A application might call it like this:

shareDocument(myDocument, user, True, True)

However, soon you decide that “sharing” a document without even allowing the other person to view is a bit useless, so you change the API call:

shareDocument(document, user, allow_edit, transfer_ownership)

You immediately update your user application to use the new argument order. However, one innocent administrative worker was still using some cached Javascript and loses access to some important documents.

Create a test version of the API that actually runs an older version than the real API

After all the chaos caused by the previous point, the client decided to provision a staging server so I could test the client application without breaking anything. I had a glimmer of hope: Perhaps the momentum will carry and the client will make more urgently necessary updates.

At first, everything goes according to plan. At least you can test your client application safely. However, your client application is not usually the one causing problems. In due time the provider releases another breaking change, then immediately starts forwarding bug reports to you. Soon, you find out the change was not pushed to the test server. You have no choice but to test in production like the good old days. You contact the provider, and they update the test server as well — A couple weeks later.

This sets a pattern. Every time, the client team publishes a breaking change to production, then complain to you that the app no longer works. I would either contact them to update the test version as well, or for an urgent change test in production.

Finale

Eventually, the app was finished, and staid stable long enough for the demo so I could collect the payment. The app would crash again next time the API changed, but I would no longer be responsible. The next day the client phoned me again…

Ultimately, the client only hurt themselves. I got paid by the hour, so I did not really care about the extra time this took. I made it very clear that the extra time taken was mostly because the API kept changing, and the client understood that.