blob: 5dcec3d1f1f124f179bf78a9569aae033a940c0b [file] [log] [blame] [view]
## Deprecation and removal
While SemVer's binary compatibility guarantees restrict the types of changes
that may be made within a library revision and make it difficult to remove an
API, there are many other ways to influence how developers interact with your
library.
### Deprecation (`@Deprecated`)
Deprecation lets a developer know that they should stop using an API or class.
All deprecations must be marked with a `@Deprecated` code annotation as well as
a `@deprecated <explanation>` docs annotation (for Java) or
[`@Deprecated(message = <explanation>)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecated/)
(for Kotlin) explaining the rationale and how the developer should migrate away
from the API.
Deprecations in Kotlin are encouraged to provide an automatic migration by
specifying the
[`replaceWith = ReplaceWith(<replacement>)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-replace-with/)
parameter to `@Deprecated` in cases where the migration is a straightforward
replacement and *may* specify
[`level = DeprecationLevel.ERROR`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecation-level/)
(source-breaking) in cases where the API on track to be fully removed.
Deprecation is an non-breaking API change that must occur in a **major** or
**minor** release.
APIs that are added during a pre-release cycle and marked as `@Deprecated`
within the same cycle, e.g. added in `alpha01` and deprecated in `alpha06`,
[must be removed](/docs/versioning.md#beta-checklist) before
moving to `beta01`.
NOTE While some APIs can safely be removed without a deprecation cycle, a full
cycle of deprecate (with replacement) and release prior to removal is *strongly
recommended* for APIs that are likely to have clients, including APIs referenced
by the Android platform build and `@RequiresOptIn` APIs that have shipped
in a public beta.
### Soft removal (`@removed` or `DeprecationLevel.HIDDEN`)
Soft removal preserves binary compatibility while preventing source code from
compiling against an API. It is a *source-breaking change* and not recommended.
Soft removals **must** do the following:
1. Mark the API as deprecated for at least one stable release prior to removal,
following language conventions for documenting why the API is being removed.
1. In a subsequent release:
* In Java sources, mark the API as `@RestrictTo(LIBRARY_GROUP_PREFIX)`.
This will remove the API from `current.txt` but retain it in
`restricted_current.txt` for compatibility checking.
* In Kotlin sources, mark the API as `@Deprecated(message = <reason>,
level = DeprecationLevel.HIDDEN)`. This will retain the API in
`current.txt` for compatibility checking.
1. For all future releases:
* Maintain binary compatibility, as the API may still be called by
existing dependent libraries.
* Maintain behavioral compatibility and existing tests.
This is a disruptive change and should be avoided when possible.
Soft removal is a source-breaking API change that must occur in a **major** or
**minor** release.
### Hard removal
Hard removal entails removing the entire implementation of an API that was
exposed in a public release. Prior to removal, an API must be marked as
`@deprecated` for a full **minor** version (`alpha`->`beta`->`rc`->stable),
prior to being hard removed.
This is a disruptive change and should be avoided when possible.
Hard removal is a binary-breaking API change that must occur in a **major**
release.
### For entire artifacts
We do not typically deprecate or remove entire artifacts; however, it may be
useful in cases where we want to halt development and focus elsewhere or
strongly discourage developers from using a library.
Halting development, either because of staffing or prioritization issues, leaves
the door open for future bug fixes or continued development. This quite simply
means we stop releasing updates but retain the source in our tree.
Deprecating an artifact provides developers with a migration path and strongly
encourages them -- through Lint warnings -- to migrate elsewhere. This is
accomplished by adding a `@Deprecated` and `@deprecated` (with migration
comment) annotation pair to *every* class and interface in the artifact.
To deprecate an entire artifact:
1. Mark every top-level API (class, interface, extension function, etc.) in the
artifact as `@Deprecated` and update the API files
([example CL](https://android-review.googlesource.com/c/platform/frameworks/support/+/1938773))
1. Schedule a release of the artifact as a new minor version. When you populate
the release notes, explain that the entire artifact has been deprecated and
will no longer receive new features or bug fixes. Include the reason for
deprecation and the migration strategy.
1. After the artifact has been released, remove the artifact from the source
tree, versions file, and tip-of-tree docs configuration
([example CL](https://android-review.googlesource.com/c/platform/frameworks/support/+/2061731/))
The fully-deprecated artifact will be released as a deprecation release -- it
will ship normally with accompanying release notes indicating the reason for
deprecation and migration strategy, and it will be the last version of the
artifact that ships. It will ship as a new minor stable release. For example, if
`1.0.0` was the last stable release, then the deprecation release will be
`1.1.0`. This is so Android Studio users will get a suggestion to update to a
new stable version, which will contain the `@deprecated` annotations.
After an artifact has been released as fully-deprecated, it can be removed from
the source tree.
#### Long-term support
Artifacts which have been fully deprecated and removed are not required to fix
any bugs -- including security issues -- which are reported after the library
has been removed from source control; however, library owners *may* utilize
release branches to provide long-term support.
When working on long-term support in a release branch, you may encounter the
following issues:
- Release metadata produced by the build system is not compatible with the
release scheduling tool
- Build targets associated with the release branch do not match targets used
by the snapped build ID
- Delta between last snapped build ID and proposed snap build ID is too large
and cannot be processed by the release branch management tool
### Discouraging usage in Play Store
[Google Play SDK Console](https://play.google.com/sdk-console/) allows library
owners to annotate specific library versions with notes, which are shown to app
developers in the Play Store Console, or permanently mark them as outdated,
which shows a warning in Play Store Console asking app developers to upgrade.
In both cases, library owners have the option to prevent app developers from
releasing apps to Play Store that have been built against specific library
versions.
Generally, Jetpack discourages the use of either notes or marking versions as
outdated. There are few cases that warrant pushing notifications to app
developers, and it is easy to abuse notes as advertising to drive adoption. As a
rule, upgrades to Jetpack libraries should be driven by the needs of app
developers.
Cases where notes may be used include:
1. The library is used directly, rather than transitively, and contains `P0` or
`P1` (ship-blocking, from the app's perspective) issues
- Transitively-included libraries should instead urge their dependent
libraries to bump their pinned dependency versions
1. The library contains ship-blocking security issues. In this case, we
recommend preventing app releases since developers may be less aware of
security issues.
1. The library was built against a pre-release SDK which has been superseded by
a finalized SDK. In this case, we recommend preventing app releases since
the library may crash or show unexpected behavior.
Cases where marking a version as outdated maybe used:
1. The library has security implications and the version is no longer receiving
security updates, e.g. the release branch has moved to the next version.
In all cases, there must be a newer stable or bugfix release of the library that
app developers can use to replace the outdated version.