Interface versioning

Posted by Peter Judge on 10-Feb-2012 08:43

Given that interfaces are meant to be fixed contracts that guarantee some behaviour, and thus should not change, how do you handle cases where they need to change?

Let's say I have an interface, IDataSource, with a Prepare method.

interface IDataSource:

  method public void Prepare(<args>).

end interface.

I now realise that the behaviour represented by Prepare needs to change; I need to add a return value for a ReuqestContext type.

interface IDataSource:

  method public RequestContext Prepare(<args>).

end interface.

Assuming there are consumers of this interface that are out of my control, this breaks things for someone. I see myself having a couple of options:

1. Create a new interface IDataSource2 and add a Prepare method that returns the RequestContext. The implementing class now needs to implement both interfaces.


interface IDataSource2:

  method public RequestContext Prepare(<args>).

end interface.

2. Add a new "PrepareRequest" method that does what I want, and leave the existing "Prepare" method alone.

interface IDataSource:

  method public void Prepare(<args>).

  method public RequestContext PrepareRequest(<args>).

end interface.

3. Add a new "Prepare" method that returns the RequestContext object as an output parameter instead of a return value.

interface IDataSource:

  method public void Prepare(<args>).

  method public void Prepare(<args>, output RequestContext).

end interface.

4. Modify the existing Prepare method to return the RequestContext and release note the change

Each of these has their own downside.

There's some discussion at http://stackoverflow.com/questions/45123/interfaces-and-versioning about this, and the highest-ranked answer basically says, "it's hard, and don't change your interfaces". There's also a comment along the lines of "classes are better than interfaces for abstraction".

Any advice, thoughts, comments?

thanks,

-- peter

All Replies

Posted by jmls on 10-Feb-2012 08:56

how about

5) use interface inheritance

/** interface1 */

using Progress.Lang.*.

interface temp.interface1: 

  method void foo(p as char).

  method void bar().

end interface.

/** interface2 */

using Progress.Lang.*.

interface temp.interface2 inherits temp.interface1: 

  method char foo(p as char).

end interface.

Posted by Peter Judge on 10-Feb-2012 09:03

Could certainly do that.

I may have a naming problem though, and certainly anyone who's implementing the interface or who - more likely - is referring to a (in this case) DataSource via a variable defined as IDataSource cannot use the new functionality without changes.

Unless i make the old inherit from the new interface, but that needs more thought.

-- peter

Posted by Thomas Mercer-Hursh on 10-Feb-2012 11:53

The part I wonder about is the "consumers out of control" part.  If this is part of some published framework and the interface is something applications using that framework will have utilized, then clearly you need to be cautious about making any change.  Indeed, the only change you can make without being disruptive is to define a new, modified interface and point people to it as the one they should be using in the future, but leave the other one in place for some reasonable time (possibly forever) so that people can transition.  *Any* change to the interface itself is going to break people's code.

But, if it is not something like this, but is internal code, then change it the *right* way, whatever that is in the circumstance, and change the impacted code to correspond.  You have to do that to have the change have impact anyway.

Posted by gus on 10-Feb-2012 12:59

It is also possible that someone using the interface does not /want/ the new functionality.

Or maybe they are busy working on somethign else now and do wish to take advantage of the new functionality. Not today but when they are ready.

Before an API is published and put to use, you better have it right. But we all make errors so change is inevitable. Minimizing the impact is especially important for APIs.

Posted by Tim Kuehn on 10-Feb-2012 13:03

If this is going into a framework, then it would be a good idea to consult a book on framework patterns.

"Implementation Patterns" has a good section discussing the pros and cons of different approaches to building and evolving a framework:

http://www.amazon.com/Implementation-Patterns-Kent-Beck/dp/0321413091/ref=sr_1_1?ie=UTF8&qid=1328900575&sr=8-1

Posted by Admin on 10-Feb-2012 13:21

unless something changed in latest  versions the method return type doesn't take part in method signature  so you will most likely have that naming conflict... can't help to  wonder what do you want to return from a method that considered void in  the first place?


alternatively you can add a method to return the 'prepared' context of  the old void method, but this will be odd for the new usage since there  will be two method calls

Posted by Peter Judge on 10-Feb-2012 14:43

that naming conflict... can't help to wonder what do you want to

return from a method that considered void in the first place?

I found myself needing more and more stuff related to the request, so I refactored it into a RequestContext type. That by itself isn't the issue though: I had previously stored the context as "CurrentTableRequest" and "CurrentDataAccessObject" (say) properties, which were set by Prepare, and cleared after the request's completion. Basically, incomplete/poor design up front, and now I'm paying for it. In this case, I could simply replace the Current* properties with one CurrentRequestContext property, but I don't like how that smells.

I hadn't meant for the specifics of the change to be important: my question was more related to the fact that designs aren't perfect first (or 2nd or 3rd ) time around, and was looking for strategies and techniques to mitigate downstream damage.

-- peter

Posted by Peter Judge on 10-Feb-2012 14:58

The part I wonder about is the "consumers out of control" part.  If this is part of some published framework and the interface is something applications using that framework will have utilized, then clearly you need to be cautious about making any change.  Indeed, the only change you can make without being disruptive is to define a new, modified interface and point people to it as the one they should be using in the future, but leave the other one in place for some reasonable time (possibly forever) so that people can transition.  *Any* change to the interface itself is going to break people's code.

This problem certainly applies to frameworks, but I think it's a far more generalised issue than that. Any 3rd-party code your application references may have this issue. Any published code which has extension or customisation points too.

-- peter

Posted by Thomas Mercer-Hursh on 10-Feb-2012 15:10

OK, so I am using "framework" to include any code which one publishes for use by third parties.  That, one has to change cautiously because one is going to upset people.  Then, I think there is no choice but to think hard and decide whether it is better to add something entirely new which won't disturb existing code or whether it is the right thing to upset people anyway.

For internal code I think it is less of an issue.

Posted by Admin on 11-Feb-2012 01:50

pjudge wrote:

I found myself needing more and more stuff related to the request, so I refactored it into a RequestContext type. That by itself isn't the issue though: I had previously stored the context as "CurrentTableRequest" and "CurrentDataAccessObject" (say) properties, which were set by Prepare, and cleared after the request's completion. Basically, incomplete/poor design up front, and now I'm paying for it. In this case, I could simply replace the Current* properties with one CurrentRequestContext property, but I don't like how that smells.

well,  incomplete design is better than no design... we can never do something  perfect anyway and the design phase should end at some point so the  implementation can start


I would say there are two different cases here... one when the interface  is meant as a contract you sign, aka external world can trust you'll  stick to it, and when it's a contract you require from others - the  external world must implement it if they want to talk to a particular  part of your system.

First case can be easily (in principle) extended by adding new methods  to the interface while keeping the old ones in place for backward  compatibility, the only issue here is with that particular case where  you want to change the return of a given method which given the naming  conflict condition could be an issue. For that particular case I guess  changing the return type from void to something else wouldn't break  anything, I expect the old code will work just fine... there must be  some 'do something' method after 'prepare' that one can call without  worrying what the outcome of the 'prepare' method was. Otherwise if you  want to change the return of a method from one data type to another  there must be a different name and I guess this makes sense since it's  not going to return apples but peaches so you must call it differently.

What can be useful here is an equivalent of 'deprecated' annotation...  this could give hints to developer not to use methods that were replaced  by something else while the old code will still work.

Second case is something that can have a more important impact, not easy  to change the terms on which someone agreed on a signed contract

In that case I would say there are only two options, one to add another  'extended' interface that is going to be implemented only by some  'clients' or to simply create a new version of the interface  specification (like the JDBC, JMS specifications for instance). If you  like to support all interface specification versions then you might want  to add the version information in there so you can know which version  is being implemented by the other side.

Posted by Peter Judge on 13-Feb-2012 07:50

I would say there are two different cases here... one when the interface  is meant as a contract you sign, aka external world can trust you'll  stick to it, and when it's a contract you require from others - the  external world must implement it if they want to talk to a particular  part of your system.

I was chatting to a friend on Friday about this topic, and he brought up a similarly-interesting distinction between interfaces and extension points. The former being more internal to the application, and the latter being important for 3rd parties/consumers of the appplication. He also reinforced what most of you have said about being careful about managing changes in public APIs.


What can be useful here is an equivalent of 'deprecated' annotation...  this could give hints to developer not to use methods that were replaced  by something else while the old code will still work.

I trypically add an annotation to this effect  - @deprecated - which has no effect on the compiler (as in Java, C# etc). But at least there's some indication for the developer.

-- peter

Posted by gus on 13-Feb-2012 11:03

It may be a big issue for internal code too, depending on your definition of internal. For example, we used various components in our prodoucts. Some of these were built by other groups at PSC and some by external companies.  All too often, it turns out to be a big job to update them with newer versions even for things made by PSC. Preserving backward compatibility is hard work.

This thread is closed