In the Java ecosystem, there is an annotation for marking code pieces as deprecated, so you do not use them in any new code. Modern IDEs can also use this to indicate that at a call location, a deprecated method is used, thus the programmer should maybe replace that call. (I will stick to methods and functions in this article, however, you can deprecate also other elements like classes, properties, …).

IntelliJ, for example, lists deprecated methods in the auto completion menu but with strikethrough text.

To say something is deprecated does not convey more than the intention to not care about that method anymore. But what does it mean? Is this method no subject to later optimizations? Did it not work as intended? Will it be removed entirely? And if so, when?

You could write Javadoc where the @deprecated-tag can be used to tell your users (especially the future-you) about your intentions.

/**
 * @deprecated  use funNew() instead
 */
@Deprecated
static void funOld() { }

More Modern Java

Have you noted, how I conveniently linked to documentation for Java 7? At the time of writing, we doubled the version numbers. Since Java 9 you can add parameters to the annotation like so:

/**
 * @deprecated  use funNew() instead
 */
@Deprecated(since="0.5.0", forRemoval=true)
static void funOld() { }

While I like forRemoval, because it helps you with prioritizing refactorizations, I am not sure what to think of the “since”. On one hand, editors do a nice job of showing the commit where each line was modified last. On the other hand, the version number gives a better sence of when the deprecation actually happened. But aside, I’d much more like an until, for example until="1". For semver fans, this means, as long as the major version is smaller or equal to 1 (ignoring inclusive-vs-exclusive), this deprecated method can be used.

Kotlin

In Java you still need to add Javadoc information to your depdecation to communicate intent. This can be forgotten (and you can be reminded by a static code checker). In Kotlin, the Deprecation annotation has a mandatory message-parameter, that forces you to write a message there.

Mandatory message for the Deprecation annotation. Also, by default, IntelliJ shows already the fucntion invocation struck through.

For such cases, where the change is not substantial but more a renaming, you can event explain to the user AND the user’s IDE how the change is to be made to resolve the deprecation. Kotlin offers ReplaceWith for exactly doing that. By adding ReplaceWith("myReplacement") to the annotation, callsites of the deprecated functions can be automatically adjusted:

When ReplaceWith is present, the quick fix menu suggests applying the replacement.

Kotlin also provides a deprecation level where you can instruct the compiler how annoying you want the deprecation to be. WARNING shows a warning during compilation while ERROR throws an error. HIDDEN is interesting, because this one will make it impossible to use the deprecated function in code, but it will remain in the compiled output.

With increasing DeprecationLevel, code gets less nice to look at.

In this figure, you see a project that depends on library lib. In lib-1.0, funOld is deprecated with level WARNING, in lib-2.0 ERROR, and finally in lib-3.0 with level HIDDEN. The difference between 2 and 3 is, that in 2, the IDE still understands funOld to be a method and thus it can be for example renamed as described above. In 3, it is just unknown, so IntelliJ would either suggest to import a matching function from somewhere else or create a new one. In all three cases, funOld is still compiled into the lib-X.jar.

Summary

Know and use the tools for deprecation that are at your hand. And be happy, that Kotlin’s deprecations are nice tools if you add ReplaceWith.