Bashing into the wall at full speed
Every programmer has to deal with external APIs at some point. At first, we’re quite anxious to try it out – something new to fiddle with! You start reading the docs, everything seems fine, endpoints are well documented. Then you jump right in and start integrating the api with your application.
The first few endpoints are integrated effortlessly. But then you notice that not everything is working as expected. Some endpoints are broken, others lack documentation or behave differently than they should. You start pulling your hair out because you waste precious hours fighting with things that were supposed to work out of the box.
We’ve been writing APIs for many, many years now, so why is this still happening?
What is a “bad API”?
I have worked with several APIs – some were good, most of them were bad. Some were created by small companies, others by quite big ones. In my opinion, these are the main indicators that you are dealing with a bad API:
API is lying about being RESTful
The docs say that the company’s API is fully RESTful. 90% of the time – the company is lying, whether they are aware of it or not. The phrase “our API is fully RESTful” has become a marketing slogan.
Some APIs do not adhere to URL patterns. Not a big deal, but still – stick to the rules!
No sandbox/test mode
If a company offers an API service, how can’t it have a sandbox mode? This is just ridiculous. A programmer should not be allowed to enter false data into the production environment. This is just wrong. Combine this with a lack of DELETE methods and you’re done.
Incomplete / incorrect documentation
Even when an API is well documented, you tend to stumble upon mistakes like a wrong argument type (e.g. integer instead of string). However, the real fun begins when an endpoint accepts a totally different set of arguments than the ones in the docs. Try guessing them all – it’s like looking for Christmas presents.
I have also been dealing with “hidden” methods: they should exist (and they do!), but somehow they are not included in the docs. I had to dig into the API’s source code in order to find them.
Lots of other bugs
The list of API programmers’ sins is quite long. To name just a few:
- minor API updates break backward compatibility
- vague error messages (e.g. “Incorrect input data”)
- wrong HTTP codes in responses (e.g. 200, when an error occurred)
I bet you could name a few more.
Why do we still create bad APIs?
You would think that in this day and age programmers should create good, proper APIs. The whole IT industry is developing at light speed. We have services like Composer, environments like Vagrant, design patterns, great frameworks, DDD, PHPUnit, Varnish… and bad APIs.
It made me think – what makes developers forget or ignore all the good practices and rules?
Yes, we’re lazy. Admit it. Embrace the truth.
You just wrote a massive collection of classes that work really great. You even covered everything with unit tests. All of a sudden you remember just one more tiny thing: documentation update. Yuck! You’re a programmer, not a novelist! Maybe just this once you can let it slip. You have more important things to do, right? You can always get back to this mundane task tomorrow. Once you are done with your new exciting quest, you can return to this horribly dull chore.
Guess what – you never come back. At least I know I don’t. You push this away from the spotlight of your attention into the deepest corners of your mind, because you just don’t want to do it.
The worst thing is that in a few days, weeks or months somebody (maybe even you) will have to use this glorious endpoint you created. And there will be no documentation on how to use it. It’s not that bad if you can read the source and figure things out (you still waste time though). It gets worse when a programmer coding for a different platform wants to use your endpoint. Things might get ugly.
Lack of time
At least this is how you see it. There is a deadline and a lot of work. The client wants to see results and you’re stuck in a jungle of commands, handlers, validators and builders.
You could speed up the whole process by executing this SQL query right here in this controller (I know, a drastic example)! You know it’s bad, but hey – the clock is ticking!
A few days later you adhere to all the rules, you write a bunch of neat classes, but you deliberately skip writing unit tests. Again, because of time shortage.
These things will backfire sooner than you think.
Change of demands
In this grim scenario, at first everything went as planned. Beautiful codebase, covered with unit tests and even documented! Ready to deploy.
You see an email from the client and everything starts to fall apart – he decided to change the behavior of several things (hey, it’s a startup). For him – minor changes, for you – a disaster.
You already know that you have to delete tons of deploy-ready code and write tons of new code. It’s very frustrating. You realize you could save, let’s say, 40% of your time, by using some nasty hacks. The client would pay for all of your work, but you’re just too angry at him to do it all by the book. You just want to get it over with.
The client doesn’t see it
That’s the biggest problem. The client doesn’t know what’s going on behind the curtains. He is only informed about the things he can experience. He can’t interact with the things you created, because for him, it’s indirect. He can only see the changes on the website and on his mobile device.
Other developers know that you did a massive amount of work (at least they should). They work with responses your API returns, exceptions it throws etc. The client works only with the things created by mobile and front-end developers.
Because of that, we tend to write our API methods quickly (thus badly), so we can get “the real work” done – implement some feature on a mobile device or website. People don’t know that complex architecture lies behind a seemingly simple feature. It’s not ignorance – it’s just a lack of knowledge.
What can we do about it?
There are some things you can do to make your API better. Here are my ideas.
Stick to a response standard
Different response formats are a real pain for API users (i.e. other developers). They expect responses to be consistent throughout the application. This means that if you return some meta-data regarding collection pagination – do it at all times. The users expect that every new endpoint will respect these rules.
The same applies to HTTP status codes. If a successful delete request results in a “204 No Content” response, don’t create another one that returns “200 OK”.
You might consider implementing a standard like JSON API – it’s really neat!
Keep the docs fresh
Once we wanted to be really cool and we created documentation entries for API methods that should be implemented in the near future. Guess what? They never came to be. Other developers were like “WTF? The docs say this endpoint should work, but it’s not there!”
Always update your docs, so the users know what endpoints are available, what HTTP verb to use and what params they can pass.
Be RESTful if you claim you are
If you state your API is RESTful – do whatever you can to make that happen. Simple as that.
Most importantly – educate your clients
The client does not know (most of the time) how much work has to be done in order to make a good API that is reliable, works properly and responds with informative error messages.
He needs to know that an API is like a car engine – without a good one, the car will look good but work horribly. You have to invest a lot of time for the API to work as expected and be well documented so that the support team (be it you or someone else) will know exactly what they are working on.
Educate the clients often, remind them that behind each feature there is an API doing all the heavy lifting.
If your client will know how much is going on behind the curtains, he will be aware that you’re not bloating the estimates just to keep the cash flow – you’re actually working very hard. This will give you time to take care of your code and docs.