In my last post I touched on how important it was to insulate consumers from the immediacy of a breaking change. Nothing you can do as a designer will allow you to create the perfect API which will never require change on the first try. What you can and should do is reduce the likelihood of the occurrence of a breaking change as much as is feasible, and then allow consumers to gradually adopt to the changes on their own schedule. In this post I’ll discuss the need for extensive content negotiation.
It has been stated, in the comments on these very guidelines no less that there is a striking similarity between the 9th and this the 11th guidelines, as both rely on or discuss content negotiation. Much like the first guideline to embrace the http protocol, the benefits, constraints, and reasons for content negotiation are sufficiently board to merit multiple discussions to be properly addressed. It is imperative a designer avoids hypermedia formats which prescribe URL patterning because this could lessen the proper attention being given to resource representation and affordance design. The goal of this discussion is to address the rest of the content negotiation constraints to prepare your designs for interaction with real traffic volume and diverse consumer demands.
As the API designer, your job is to provide the simplest service you possibly can to your consumers. CRUD APIs like OAS (swagger) often struggle with complex designs when domain functionality doesn’t map to 4 methods very well. Other solutions like GraphQL provide excellent solutions to captive audiences and internal services, but for external consumers often result in the same poor consumer experience. Quite simply the act of consuming the service correctly requires too much knowledge about how the service is built. So how do you avoid making these same mistakes with hypermedia APIs? You allow your consumers to interact with your service just about any way they want. The fact is you will never be able to guess all the particular ways a consumer would want to interact with your service or tailor their requests, so don’t try. The solution is to build your service as generic as possible and allow the consumers to choose the interaction mediums will be used.
What all should be negotiated? The short answer is everything you can reasonably support which adds to the consumer experience. A longer non exhaustive list of potential negotiated points:
- Hypermedia Format (Content-Type)
- Filter Strategy
- Query Strategy
- Pagination Strategy
- Cache Control Strategy
- Goals
- Vocabulary
- Sparse Fieldsets
- Representation or Document Shaping
It’s a long list, does your service really need to support all of those negotiation points? It should aim to support all of these and more if they are reasonable and feasible to your service domain. Yes, this adds a lot of complexity but it’s crucial to focus on the consumer experience, and the long term payoff of creating a service which will happily satisfy the consumer needs for years to come.
These negotiation points are all critical to supporting a wide breadth of consumers, but they are also central to providing service flexibility over time. A service designed from the beginning to be generic, and support a wide range of many different properties already has the capability to support one more option in any particular property. When a new hypermedia format comes out, or a new standard filter strategy, your service already provides multiple options for this properties and supporting the change is nothing more than plugging in the appropriate functionality. You can’t know what formats will be wanted in 5 years, but your service has been designed to account for changes over time, and the required upkeep is vastly lower than any alternative presented to date.
Design your API to negotiate with your consumers as much as possible, and you will have an enduring service your consumers will love to use for years.