22

Say you have an interface IFoo:

public interface IFoo {
    void Bar(string s);
    int Quux(object o);
}

In version 2 of your API, you need to add a method Glarg to this interface. How do you do so without breaking your existing API users and maintaining backwards compatibility? This is mainly aimed at .NET, but can apply to other frameworks & languages as well.

3
  • You can add without problems. The problems comes when you change/remove something that was already there.
    – rlperez
    Commented Mar 9, 2012 at 12:56
  • 1
    @Rig: In C# at least, you will get a compile error if you add a method to an interface and don't add it to classes that implement that interface.
    – Malice
    Commented Mar 9, 2012 at 14:34
  • Well, that's true. I was thinking more from the user class scenario which may be of minimal chaos compared to changing a method signature or deleting one. So I suppose it could lead to some work if you needed to add to your interface.
    – rlperez
    Commented Mar 9, 2012 at 14:44

5 Answers 5

9

In version 2 of your API, you need to add a method Glarg to this interface.

Why?

The interfaces defined for use with an API have two entirely different roles:

  1. Dependency inversion - such interfaces are consumed by your API. They allow client code to create plugins etc.
  2. Abstraction - such interfaces are returned by your API and hide implementation details of the returned objects.

Now for a given version of an API, the same interface may act as both. Still, in future versions, this can be decoupled.

  1. You want to extract more information from the interface you consume. To enhance performance, or add flexibility or whatever. Define a new interface, possibly derived from the old one, and build a separate method consuming it. AFAIK most .NET languages allow method overloading, so this can happen without adding much clutter.
  2. You want to "return more", i.e. the abstraction of a "richer" object from your API. Here you're having two choices:

    • You can reasonably assume, that client code will not have its own implementors of the interface. Under this assumption it is safe to add your extensions to the existing interface.
    • Define a new interface, if possible derived from the previous one. If such derivation is impossible, create separate methods to query for instances of the new interface or use composition:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      
15

DirectX added version numbers to its interfaces. In your case, the solution would be something like

public interface IFoo2 : IFoo
{
    void Glarg();
}

The API would still refer to IFoo, and to IFoo2 only in methods etc where IFoo2 functionality is required.

API implementation should check in existing (=version 1) methods whether an IFoo parameter object actually implements IFoo2, if method semantics are different for IFoo2.

3

Adding a new method (or methods) to your API should be done in such a way that it does not have any side effects on the existing API. Most importantly, someone who continues to use the old API as if the new API does not exist, should be unaffected by it. Using the old API should not have unexpected side effects on the new API either.

If any of the existing methods in the API are superseded by the new ones, do not remove them straight away. Mark them as deprecated and provide an explanation as to what should be used instead. That gives users of your code warning that future versions may no longer support it instead of breaking their code without warning.

If the new and old APIs are incompatible and cannot live together without unwanted side effects, separate them and document that if the new API is to be adopted, the old API must be retired completely. This is less desirable as there will always be someone that attempts to use both and gets frustrated when it doesn't work.

Since you asked about .NET specifically you may want to read this article about deprecation in .NET, which links to the ObsoleteAttribute (used in the following example):

using System;

public sealed class App {
   static void Main() {      
      // The line below causes the compiler to issue a warning:
      // 'App.SomeDeprecatedMethod()' is obsolete: 'Do not call this method.'
      SomeDeprecatedMethod();
   }

   // The method below is marked with the ObsoleteAttribute. 
   // Any code that attempts to call this method will get a warning.
   [Obsolete("Do not call this method.")]
   private static void SomeDeprecatedMethod() { }
}
2

Public interface changes involve breakage. The common strategy is to do these only on major versions and after a freeze period (so it doesn't happen on a whim). You may get away without breaking your clients if you are adding you additions into a new interface (and your implementation can provide both on the same class). That's not ideal, and if you keep doing it you will have a mess.

With other kinds of modification though (removing methods, changing signatures), your are stuck.

1
  • 2
    You can preemptively reserve a prefix for future method names and warn all users that they should not use that namespace, but even that makes for an inelegant API. In general, parent is absolutely right: removal (and often addition) of methods will break existing users, and there's nothing you can do about that except plan it wisely. Commented Mar 9, 2012 at 10:16
1

An interface is a contract, therefore it should not have versioning. What happens if a football player gets a new contract? Is the old one still valid? No. If one changes the interface, the contract changes and the previous contract (interface) is no longer valid.

Although you could use the IFoo2 strategy, eventually that will become messy when you have:

  • IFoo2
  • IFoo3
  • IFoo4
  • etc.

Yuck.

An API is different. I give library of code to use. Next month I give you an updated library. As another poster has said, don't break what I am already using, just add new functionality/methods.

If you want to version something, use an abtract class instead of an interface.

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.