Tuesday, November 26, 2013

A Long Post for a Simple Rule

The other day, my buddy Ryan asked me a question about a program he was working on:
Ryan: so say I have three lists of (different) objects in a helper class to deserialize JSON to
Ryan: then these objects get saved to a database
Ryan: in the current version of our platform, all three of these lists will be populated, but in the previous version only two lists get populated
Ryan: would it be better to create a new helper object just for the backwards-compatible API call? or use a variety of if statements to check whether that one list is null or not and update the DB accordingly?
 As I mentioned in my post on the XY problem, I needed to get more into the details about the problem:
Ed: umm
Ed: depends?
Ed: you're adding a new list to this object?
Ed: or was it always there, but is sometimes filled?
Ryan: basically JSON data from a HTTP request is automatically deserialized into an object, and depending on API version a client will either send 2 or 3 arrays of objects
Ryan: would you create two different objects to deserialize to or use one object and just check whether one of the lists has been populated
At this point, I was pretty sure what it was he was trying to do. He was rewriting a service with a new API, but it had to be backwards compatible. But we were still talking about a problem one level removed from the important issue at play: changing the API.
Ed: is the api changing? is the client changing?
Ryan: the API is changing, but the client may not change
Ed: So the question you have is, On the backend of the API, do you create separate classes to represent data for APIv1 and APIv2?
Ryan: right
Ed: what does APIv1 send
Ed: right now
Ed: 2 arrays or 3
Ryan: v1 sends 2, v2 sends 3
This is a sneaky issue. One might think that changing an API to send additional data back is not really a breaking change. I mean, if they're still getting X and Y, who cares if they get Z as well?

You should care! You can't predict how someone else will use your API. Maybe they'll write some terrible code that would throw exceptions if there are three arrays returned. Even if the consumers of your API are your coworkers, you have to expect that your consumers will rely on every single thing your API does.

And so, I began to dissuade Ryan of his folly:
Ed: so if you use the same object, you'd change APIv1?
Ryan: no, v2 only extends on v1, doesn't change it
Ed: but v1 sends 2 arrays now. if you make it send 3 arrays, you are changing v1
Ryan: I'm deserializing to the same object via two different controllers in two different namespaces
Ed: doesn't matter. Don't change v1
Ed: and changing the return value, even if it means adding an always-empty array, is changing the API
Like I said, this one's sneaky. We as programmers tend to think about how we would use our own code. But an API, even if only available to a limited set of people, is a public interface, and people will do dumb things with it.

Conclusion:
Ed: a good rule of thumb is Never change the API
Ryan: yeah I guess I would be changing it, wouldn't I
Ryan: damn
Ed: yes
Ed: that concludes today's lesson

No comments:

All rights reserved. Take that!