9

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.

4
  • 1
    I don't see a good way to do that without duplicating code - Your new API can always call methods in your old API, or vice versa. Commented Oct 10, 2012 at 19:16
  • 2
    AutoMapper to the rescue, unfortunately yes- you need distinct versions of each contract, don't forget all objects referenced by your contract are a part of that contract. The result is your actual implementation should have it's own single version of models, and you need to transform the single version to the various contract versions. AutoMapper can help you here as well as making the internal models smarter than the contract models. In the absence of AutoMapper, I've used extension methods to create simple translations between internal models and contract models. Commented Oct 10, 2012 at 19:21
  • Is there a specific platform/context for this? (ie, DLL's, a REST API, etc) Commented Oct 10, 2012 at 22:01
  • .NET, with MVC and Webforms ui, dll classes. We have both a rest and soap API.
    – Case
    Commented Oct 10, 2012 at 22:20

1 Answer 1

7

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).

4
  • Thanks for this very detailed answer. Do you know of any examples of open source projects that I might peruse to learn how existing projects have done this? I'd like to see some concrete examples of how they have organized code that allows them to do this without just copying their various POCO construction code into the various version objects, since if you call shared methods, you'll have to split it out of the shared method to be able to edit that object for the versioned object.
    – Case
    Commented Oct 12, 2012 at 18:27
  • 1
    I"m not aware of any good examples off the top of my head. If I have some time this weekend I could create a test app to throw on GitHub to show how some of these things could be implemented. Commented Oct 12, 2012 at 19:01
  • 1
    The tricky part is that from a high level there are so many different ways to try to minimize coupling between clients and servers. WCF has an interface called IExtensibleDataObject which allows you to pass data that is not in the contract from the client and send it over the wire to the server. Google created Protobuf for communication between systems (there are open source implementations for .NET, Java, etc). In addition there are many message based systems that can also work (assuming that your process can be performed asynchronously). Commented Oct 12, 2012 at 19:06
  • Might not be a bad idea to show a specific example of the code duplication that you are trying to remove (as a new stack overflow question) and see what solutions the community comes up with. It's difficult to answer the question in general terms; so a specific scenario might be better. Commented Oct 12, 2012 at 19:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.