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.