API Design – Versioning APIs

api-designversioning

Suppose that you have a large project supported by an API base. The project also ships a public API that end(ish) users can use.

Sometimes you need to make changes to the API base that supports your project. For example, you need to add a feature that needs an API change, a new method, or requires altering of one of the objects, or the format of one of those objects, passed to or from the API.

Assuming that you are also using these objects in your public API, the public objects will also change any time you do this, which is undesirable as your clients may rely on the API objects remaining identical for their parsing code to work. (cough C++ WSDL clients…)

So one potential solution is to version the API. But when we say "version" the API, it sounds like this also must mean to version the API objects as well as well as providing duplicate method calls for each changed method signature. So I would then have a plain old clr object for each version of my api, which again seems undesirable. And even if I do this, I surely won't be building each object from scratch as that would end up with vast amounts of duplicated code. Rather, the API is likely to extend the private objects we are using for our base API, but then we run into the same problem because added properties would also be available in the public API when they are not supposed to be.

So what is some sanity that is usually applied to this situation? I know many public services such as Git for Windows maintains a versioned API, but I'm having trouble imagining an architecture that supports this without vast amounts of duplicate code covering the various versioned methods and input/output objects.

I'm aware that processes such as semantic versioning attempt to put some sanity on when public API breaks should occur. The problem is more that it seems like many or most changes require breaking the public API if the objects aren't more separated, but I don't see a good way to do that without duplicating code.

Best Answer

When maintaining an API that is used by third parties it is inevitable that you will need to make changes. The level of complexity will depend on the type of change that is occuring. These are the main scenarios that come up:

  1. New Functionality Added to Existing API
  2. Old Functionality Deprecated From API
  3. Existing Functionality In API Changing In Some Way

New Functionality Adding To Existing API

This is the easiest scenario to support. Adding new methods to an API should not require any changes to existing clients. This is safe to deploy for the clients that need the new functionality as it has no updates for any existing client.

Old Functionality Deprecated From API

In this scenario you must communicate to existing consumers of your API that the functionality will not be supported long term. Until you either drop support for the old functionality (or until all clients have upgraded to the new functionality) you must keep the old and new API functionality at the same time. If it's a library that's provided most languages have a way to mark old methods as obsolete/deprecated. If it's a third party service of some kind it's usually best to have different endpoints for the old/new functionality.

Existing Functionality In API Changing In Some Way

This scenario will depend on the type of change. If input parameters no longer need to be used then you can just update the service/library to ignore the now extra data. In a library it would be to have the overloaded method internally call the new method that requires fewer parameters. In a hosted service you have the endpoint ignore extra data and the it can service both types of clients and run the same logic.

If the existing functionality needs to add new required elements then you must have two endpoints/methods for your service/library. Until clients update you need to support both versions.

Other Thoughts

Rather, the API is likely to extend the private objects we are using for our base API, but then we run into the same problem because added properties would also be available in the public API when they are not supposed to be.

Do not expose internal private objects through your library/service. Create your own types and map the the internal implementation. This will allow you make internal changes and minimize the amount of updating the external clients need to do.

The problem is more that it seems like many or most changes require breaking the public API if the objects aren't more separated, but I don't see a good way to do that without duplicating code.

The API whether it is a service or library needs to be stable at the integration point with the clients. The more time you take to identify what the inputs and outputs should be and keep them as separate entities will save you a lot of headaches down the road. Make the API contract its own separate entity and map to the classes that provide the real work. The time saved when internal implementations change should more that offset the extra time it took to define the extra interface.

Don't view this step as "duplicating code". While similar, they are separate entities that are worth the while to create. While external API changes almost always require a corresponding change to internal implementation, internal implementation changes shouldn't always change the external API.

Example

Suppose you are providing a payment processing solution. You are using PaymentProviderA to do credit card transactions. Later on you get a better rate through PaymentProviderB's payment processor. If your API exposed Credit Card / Bill Address fields of your type instead of PaymentProviderA's representation then the API change is 0 as the interface remained the same (hopefully anyway, if PaymentProviderB requires data that was not required by PaymentProviderA then you need to chose to either support both or keep the worse rate with PaymentProviderA).

Related Topic