views:

256

answers:

11

Suppose you are working on some software that has an interface used by other software. It might be a library or a website or a command line program or an operating system. But at least one other developer uses your interface to get some functionality so they won't have to do the work in their own code. Is there ever a time when it's acceptable to break that public interface?


Obviously an interface change might unintentionally cause some program out there to break. That's just bad luck, bad documentation or bad usage. Here are the sort of breaks I'm thinking about:

  1. An new parameter is required to be supplied by the caller.

  2. The function name or URL is changed and the old name doesn't exist.

  3. Error/return codes change meaning (e.g. 1 used to mean fail and now it means one result).

  4. New side effects are added or old ones removed.

  5. The result set format is radically changed (e.g. HTML to PDF).

  6. A long-standing bug that developers have coded around gets fixed.

  7. Results are "improved" in ways that are not backward compatible in all caller code (e.g. 32 to 64 bit results).

My assumption is that these sort of changes are always bad practice after the interface has been public for a reasonable "beta" period. Am I right?

A: 

If the user has the ability to forgo upgrade to the new version and you warn them of this change, then it is acceptable to change the interface.

Milhous
A: 

The only answers you'll get to this are personal feelings, and mine is that you're right, once you have provided a public interface through its beta period and into implementation, you don't ever break it. If you need to do any of the seven things you listed, you add a new method, or a new class, or whatever you need to provide that new functionality; it's trivially easy to do, and then lets the users of your library choose which functionality to implement, upgrade to your latest library without breaking their code, and migrate functionality to your "new" implementation over time.

But again, that's just how I feel; I'm sure that others might feel differently.

delfuego
+8  A: 

Almost never.

The only excuse I can think of is if the interface is horribly broken by design, but even then it's probably best to deprecate it rather than change it.

Instead,make your changes backwards-compatible. Use approaches such as having version numbers in the inputs and outputs.

Or, replace a deprecated interface with a whole new interface: "MyFunction() will continue to work in the same broken way, but its continued use is discouraged. Use MyFunction2() to benefit from improvements x, y, z.

slim
+2  A: 

I generally try to deprecate the parts of the interface for 2 releases before finally deleting them. When confronted with changes that don't seem to have a clean migration path, I either kludge in backwards-compatibility (which is only temporary), or change the major revision number, but leave continue supporting the previous interface on a separate branch for a "fair" period of time.

Steve Moyer
+1  A: 

If you have a certain degree of control over your release schedule, I think at a minimum you need to provide a period during which the old interface is publically marked as 'deprecated' with clear compile warnings to indicate that the current functionality will be disappearing in a future release. Mere documentation warnings aren't sufficient here.

It would be best, if you're going to make such a change, to continue to make a 'classic' version of the API remain available for developers who rely on it.

Jim Kiley
+1  A: 

Any time you change an interface that someone is currently using, you're causing them extra work. You have to balance out the downside of them doing work with some upside, a significant improvement in functionality, or bug fixes that were not possible with the old interface. Some other mitigating factors to reduce the impact are:

  1. Giving them a long period of notice before making the change.
  2. Maintaining backwards compatibility.
  3. Providing a good migration guide.
  4. Being more important to them than they are to you so they're forced to change to stay in business.
sk
+1  A: 

It's one thing to stop supporting an interface, but changing a public one is just wrong.

Aardvark
+2  A: 

There's always the Microsoft solution - keep the old interface around forever, and add CreateWindowEx(), or the equivalent. This is more work for you, but keeps customers happy.

Another popular "solution" is reserved parameters, which are "unused, but must be set to NULL", or somnething similar. Then when you need to add more parameters, you package them up into a struct, and pass that as the previously-unused parameter.

Mark Bessey
+2  A: 

When is it acceptable to break interface? Never!!

Almost all the books on COM address this exact issue and how COM is conceptualized to solve this issue!

Ofcourse change is inevitable and for most cases, its for a good reason. But you always provide a newer version which provides EXACT same functionality as old version AND with newer interface to use improved functionality.

Providing Backward compatiability almost always gets messier if you dont do in the proper way!

Prakash
A: 

An argument could be made for changing an interface when only a limited group has access to it. For instance, if I write a library for a small programming team, it might be alright to change the interface and hunt down all the places the interface is used and change them.

In fact, I'm guilty of that practice. But I now think it was a mistake. As delfuego pointed out, the functionality can usually be moved to a new interface and the old interface can be phased out over time. The only reason not to do this is namespace and code bloat. The extra code can sometimes be avoided by making one or the other interface a wrapper over the other. (Often the old interface will wrap the new interface since usually it's often easy to throw new functionality away. Sometime both interfaces will call some utility procedure.)

Namespace pollution is a more difficult problem. It would be nice to call strcpy rather than strlcpy (or is it strncpy?). But I think programmers often over-emphasize "clean" code to the detriment of backward compatibility. In a surprisingly number of cases, the old interface can still be useful in new code. So maybe a better way to phrase the situation is to call it namespace enrichment.

In the case I mentioned before with a really small team, the big problem I ran into was testing the interface became a race condition. So I changed a library function and all the code that used it broke. No problem yet because I can easily change that calling code to conform to the new interface. When I started testing the new interface, I found and fixed a bug in the library. The Months later another bug in some calling code cropped and I rolled back to a previous version. Unfortunately, the previous version used the old interface, so the code broke again. If I'd set up a new interface instead of changing it, the testing path would be clear: change a call and test it. Rolling back code might cause the old interface to be used, but wouldn't break anything.

At some point, as others have pointed out, it might be reasonable to depreciate and even remove the old interface. But the timeline would need to be very long so that all the problems could be sorted out.

Jon Ericson
+1  A: 

This is why it is always a good idea to spend the time needed to come up with a good interface design to begin with. While your doing that I would also consider how you will add features to your interface in the future.

In this instance if you warned people that your interface was in beta. As long as you can guarantee that programs developed with the old interface will no longer compile, I don't see too many problems with changing the interface.

Brad Gilbert