As someone who consumes many web service APIs – both internal and external – the recent trend in the web development community to forgo writing API documentation has got me worried. While I do understand that publishing and maintaining documentation can be a hassle, APIs with no documentation can quickly become a bottleneck in a team’s workflow, inhibiting the team’s ability to scale, outsource, or distribute themselves geographically. In this article, I’ll walk you through a partially-fictionalized version of a disastrous experience I had working on distributed system whose APIs were largely undocumented.
Essentially, I had been contracted to build an iOS client for a company based out of London (and I’m in Los Angeles). They wanted a mobile user interface for their REST API, which facilitated the management of an individual’s contacts (the “Contact Service”). I was assured that the API was easy to understand; it followed “RESTful conventions,” exposing CRUD operations on its resources via familiar HTTP verbs. The mobile client would be open-sourced but the API itself was closed; I would not have access to the repository for the API whose interface I would be consuming.
My first day on the project consisted of getting my development environment up and running. I installed Xcode, set up my test rig, and did all the usual stuff that happens at the beginning of a project. By mid-afternoon, I was ready to start working on the mobile client. I stubbed out the API client’s interface based on what I’d seen in the comps and what I knew about the API from talking to my contact on the Rails team – but this is about as far as I could get without knowing more about the URIs that I would be hitting. In an effort to un-stick myself, email the lead developer on the Contact Service team, asking her for a list of the the URIs that I’ll be working with.
The next morning, I open up my email and see a response from the lead developer in London. She’s provided a list of API endpoints such that I’ll know where to send the API client’s HTTP requests:
Her list of URIs, unfortunately, told me neither about the request body that the API expected to receive from clients, nor the response that I could expect to get in return. I was stuck, again. I replied to her email, this time asking for a detailed description of the API request and response payloads.
By now, the Rails team was sick of fielding my questions and I was being asked by my boss why it is was I hadn’t yet made any progress on the API client. After a tense conference call with the team in London, the client agreed that the best way for me to get the answers I needed was to get access to the Git repository so that I might “just look at the source code” myself. The plan was to grant me limited access to the Contact Service sources, which I’d then use to divine the API’s request and response formats. This sounded well and good – except for the fact that I was totally unfamiliar with Ruby and Rails!
With the Contact Service source code now sitting on my hard drive, I was ready to really start working on the iOS client. I spent much of the day googling – hey! I write mobile apps, not Ruby web services – and eventually located the
Contact model. This class detailed the constraints that needed to be satisfied in order for a for a contact to be created… as a row in a database.
To confirm that this class represented the acceptable format of the JSON object I would be posting to the collection resource (to create a contact), I issued an exploratory HTTP POST with cURL:
…which was met with an HTTP 422 “Unprocessable Entity”:
As it turned out,
last_name were required by the API… not
full_name. The server combined the two fields into a
full_name property to be passed to the database – something that I found out only after digging through lots of Ruby code. I corrected the issue, and was finally able to successfully create a contact:
In the end, I was able to reverse-engineer the API request and response bodies by digging through the Rails code. Were this API to have been properly documented, I would have been able to spend my time (and the client’s money) working on what I’d originally been contracted to work on – which was to build an iOS client. Instead, I spent my time digging down into the persistence layer of a codebase I didn’t understand in order to properly form an HTTP request to its web interface. Nearly four days had passed since I had started on the project and I’d written no more than a hundred lines of real code. What a waste!
An Explicit Contract
When a programmer deploys an API – either internally or externally – they have formed an implicit contract with the consumers of that API; there exists some combination of request parameters, body, and headers that when sent to the API will result in some stuff getting done. In the aforementioned story, I was forced to dig into the API’s implementation in order to reverse-engineer its implicit contract. I was lucky to have been given access to the API’s version control system and that I could riddle the technology used to implement the API; if the source were to have been off-limits to me – or implemented in a language I didn’t understand – I would have been dead in the water.
To save developers (both current and future) from having to perform this discovery process themselves, I recommend that developers publish documentation – an explicit contract – outlining the basics of interacting with their API. This API documentation should be maintained by the team much like a suite of automation tests; a failure to keep it up to date should be treated like a break of the build. The output of this effort will be a document (or set of documents) that API consumers can monitor for changes – with conversations taking place if (when!) the interface needs to be changed.
This documentation can take on many forms. At minimum, I recommend a simple markdown document enumerating the supported HTTP methods on each URI along with example request and response payloads. Each field’s data type should be provided, along with notes explaining any non-obvious constraints.
From my perspective, the lack of documentation in this system greatly reduced the client’s ability to distribute their team geographically, outsource (to contract workers like me), or to allow for any sort of functional specialization (Contact Service/iOS developers). Without documentation, time was wasted trading emails, dialing in to teleconferences, and sifting through source code as I struggled to understand how to interact with the API that I’d been tasked with wrapping.
Developers: We’ve got to be able to do better than this! I urge you to push back when you hear a coworker say that they “don’t write documentation” for their APIs. That’s lazy (at best), and irresponsible (at worst). Create a culture of building and maintaining API documentation early in a project; it’s a difficult thing to introduce late in the game. The other developers on your team – the ones that you’ll be able to add later, when you need them – will surely thank you for it.
Since writing this article, several coworkers have asked me: “At what point should I start documenting an API?” While there are no universally-applicable set of rules, the following guidelines have served me well:
- If are a contractor or consultant who will eventually hand the maintenance of the API off to other developers – write docs
- If the team contains programmers who will dwell primarily on one side of the API wall (say, iOS developers) – write docs
- If the API server is closed source – write docs
- If you’re creating a public-facing API – write docs
- If your team is larger than 3 people – write docs