Today, I’d like to tackle a subject that seems to be misunderstood by a sizable fraction of Java developers: the lifecycle of features in Java.
Product management 101
Before focusing on Java, let’s talk in general. Suppose you design a language, a framework, a library, anything that is provided to third-partys. Because you did a good job, this software is now widely used.
Because this is the real world, after some time, you realize a specific feature you offer is not good enough. Let’s call this feature feature-v1. You did a good job, nicely separating API from implementation details. If it would be a simple bug, it would be easy: just change implementation details and you are done. But it’s not the case. The problem lies in the API. And the API is already in use by many users.
Thus, you cannot just replace the feature with an improved one. Let’s call this one feature-v2. Doing so would prevent users from easily migrating to the new version. You need to add feature-v2 while keeping feature-v1. This way, users can upgrade and benefit from improvements in other areas while not changing their codebase. This is backward compatibility.
By doing so, you expect users to slowly migrate to feature-v2 because it’s so much better. Unfortunately, it doesn’t seem to happen. At least not at the pace you were expecting. Worse, some new users don’t seem to understand the value of feature-v2 over feature-v1, and they use it. Just at the same time as you were ready to deliver feature-v3! It starts to be a maintenance nightmare: of course, costs associated with the feature are going to the roof. But most software developers want to work on the newest version, not on the maintenance of older versions…
That cannot continue for long. The first action to undertake is to stop new users from using feature-v1. While this cannot be prevented per se, they should at least be informed: feature-v1 is not the latest and greatest. This process is called deprecation. It’s a bold and clear statement to everyone that a feature version has no future, at least in its current form.
Finally, after some time, you can remove feature-v1: obviously, this is known removal.
Deprecation in Java
The original way to mark a class or a class member for deprecation was through a JavaDoc tag. Here’s how it was implemented:
/**
* @deprecated As of release 1.1, replaced by {@link #bar()}
*/
public void foo() { }
This approach has several limitations:
- If you don’t have access to the JavaDocs, you don’t have the deprecation information as well. For example, if you’re using Maven and an IDE, you’ve to configure the IDE to download JavaDocs along with the binary JAR. Though it’s a one-time configuration, many developers don’t do it
- It depends on the overall quality of the JavaDocs.
If you’ve used long-lived libraries/frameworks a bit, you probably stumbled more than once upon
@deprecated
without any more information./** @deprecated */ public void foo() { }
Good luck to know what you’ve to use instead…
Along with annotations, Java 5 brought @Deprecated
.
On one hand, it has runtime retention policy;
hence, it’s accessible along with the binary.
On the other hand, it doesn’t solve the quality issue.
JEP 277, included in Java 9, tried to improve the situation.
Unfortunately, in the end, it just added two attributes to @Deprecated
:
public @interface Deprecated {
String since() default "";
boolean forRemoval() default false;
}
That doesn’t solve the lack of quality issue either.
Removal in Java
For a looooong time, there has been no feature removal in Java.
Not one.
Zilch.
Only deprecation.
For example, you can still check the java.util.Date
:
it has methods deprecated since Java 1.1 still living and kicking.
This version was released in 1997.
Imagine that!
IMHO, this long-term approach toward backward compatibility is one of the main reason why Java has been so successful in the enterprise. On the downside, it’s also one of the reason why younger languages are able to be more innovative. The larger the user base, the more important is backward compatibility.
In fact, APIs started to be removed only since Java 9 (to the extent of my knowledge, please correct me if I’m wrong). There are some APIs planned to be removed in Java 10 too.
Conclusion
Managing the lifecycle of a feature is not easy. A fine balance must be struck between innovation and backward compatibility. Think about all of the above the next time you’re bitching that Java doesn’t have reified generics.