When developing an API for some software product/service, many times revisions are made and existing members (properties/methods) are marked as obsolete just because they might already be used by some client and removing them would introduce breaking changes. My question is, how long do you wait before you totally remove such a member? What does it depend on? Time, product version, customer input? The end result I see happening is that obsolete members exist for years. So have you removed them at some point? What is your experience?
It depends on what behaviour people who use the old API will get with the newer versions. If you can still give the expected behaviour (minus new functionality) then I don't see any reason to ever remove "obsolete" members. However if you're just moving the failure to runtime (e.g. by throwing a NotImpl
exception) then it's probably better to cause the client application to break at compiler time so that they can fix it early on.
This can be considered to be the Raymond Chen school of thought, maintain backwards compatibility at (almost) all cost.
It depends on what you tell your customers up front, before you make them obsolete.
One example: "Our 2.0 release has breaking changes. A patch is made available for the 1.1 product that has marked obsolete/depricated all the breaking changes for the 2.0 release."
Another example: "There will be no braking changes, unless it has been marked obsolete/depricated for at least two major releases."
Deprecated methods should be removed after 2-3 official revisions of the library. For example if we had method foo() in verion 1.0, then in 1.5 it is marked as obsolete. In 1.6 (the next released) it should still be there in case some people still use it and didn't mention that it is deprecated in 1.5. In 1.7 the method could be removed and the people should use the new API if they want the new library version.
For large projects it might be annoying to change some libs in the middle of the development and in such case concrete decision should be made if really the new version of the library is needed or not.
In any case it should be well documented what is the new API replacement
Consider it this way, customer A downloads the latest version of you library file or frame work. He hits compile on this machine and suddenly he see thousands of errors because the member file or function does no longer exist. From this point on, you've given the customer a reason why not to upgrade to your new version and to stay with the old version.
Raymond Chen answers this the best with his blog about win32 API,
Though, our experience in our software house has been, once the API has been written we have to carry the API to the end of the product life cycle. To help users to new versions, we provide backwards compatibility with the old commands in the new framework.
When I worked on an API (in C#) we marked methods and properties as obsolete and let them stay for a full release before removing them. IE a method being marked obsolete in version 1.1 would stay in 1.2 and be removed in 1.3.
The most important thing in my mind is communicating the upcoming change. If people know the change is coming they will have a chance to react. Don't forget to also communicate the new ways of doing things too. Both in writing and if you use C#-style intellisense documentation.
A few times we stayed a deletion due to overwhelming user feedback. If all your users are complaining you will have to have very good arguments on hand to still go ahead with the deletion.
In my mind it is important to delete obsolete members from the API to prevent API-usage that can be harmful or the "wrong" way of doing things.
Usually I wait one major revision, but your versioning system may vary. E.g. if I tag some parts of 1.x as deprecated in 2.x, I will drop them in 3.x. Just make sure the 2.x release notes say "Everything marked deprecated in this version will be removed in 3.x" and the 3.x release notes say "Everything that has been tagged deprecated in 2.x has now been removed. If you still use deprecated methods, your code will break if you upgrade to 3.x", thus clients know, if they plan to go 3.x, they first need to modify their client using 2.x in such a way that no deprecated stuff is used anymore.
Note: You should never tag anything deprecated, if you don't have an alternative ready. E.g. if you have a method in 2.x, that you consider very poor and you want to replace it by a new one in 3.x, but in 2.x this new method is still not there, you shouldn't tag the old method deprecated in 2.x, because how can user avoid using it, if the new one is still not there? Instead, you create a new one in 3.x, keep the old one in 3.x and mark the old one deprecated in 3.x, so you can drop it in 4.x
One word regarding my versioning system (it's pretty simple):
A.B.C - three digits
- C is bugfix number. 1.3.1 and 1.3.2 are identical in functionality, just bugs got fixed.
- B is the minor revision. 1.3 and 1.4 are pretty much identical, but 1.4 has some new functionality that 1.3 did not have. However, no old functionality was changed or removed.
- A is the major revision. Major releases are always done when not just bugs got fixed and not just new functionality got added, but when old functionality changed or old functionality got removed.
That means for clients:
- It is always safe to upgrade to a higher bugfix number; nothing has changed, the code is just less buggy. It is also safe to downgrade, code will just be more buggy, though.
- It is always safe to upgrade to a higher minor revision, everything that worked before will still work. It is not safe to downgrade to a lower minor revision if you use any functionality, that did not exist there (you have to check upfront).
- It is never safe to upgrade to a major release, everything is subject to change! Nor is it safe to downgrade!