| Field | Value |
|---|---|
| DIP: | 1013 |
| Review Count: | 2 |
| Author: | Jack Stouffer jack@jackstouffer.com, Jonathan M Davis jmdavis@jmdavisprog.com |
| Implementation: | N/A |
| Status: | Accepted |
In order to incrementally improve D or its standard library, it's often necessary to mark features or functions for future removal. This document proposes a standardized process for language maintainers to remove public features. This process would be used across DMD, DRuntime, and Phobos.
- Rationale
- Terminology
- Description
- Summary of the Steps in the Normal Deprecation Process
- Copyright & License
- Reviews
There is general disagreement on the best or accepted way to remove public features. Each deprecation is handled with slight variations depending on who is writing the pull request. Standardizing the process makes sure that deprecations are done very publicly and carefully, to minimize breakage and to provide clear fixes for user code.
The following lays out the standard procedure for deprecating a part of D. It is roughly the procedure that has been followed with Phobos for several years, but this formalizes and clarifies it with some minor modifications.
In the typical case, users should be given at least ten non-patch releases before a deprecated feature or symbol is removed. This gives users time to update their code without forcing them to do so as soon as possible while still not retaining deprecated features or symbols long term.
More releases can be given if deemed appropriate. In particular, if a feature or symbol has existed for a long time and is heavily used, then it may make sense to retain it longer. In such cases, the feature or symbol should probably be undocumented within approximately the standard deprecation period of ten non-patch releases to reduce the likelihood of its use in new code. In general, deprecated features and symbols should not be left around long term. Doing so imparts a maintenance burden on the maintainers of D compilers and the standard library. It also increases the likelihood that the feature or symbol will continue to be used long beyond the point that it would ideally no longer be used and, in some cases, it can actively prevent new features or symbols from being added. Ideally, deprecated features and symbols would be removed from the language or standard library in a timely manner so that they no longer need to be maintained and D programmers no longer need to deal with them in any code bases that they work on. Retaining deprecated features or symbols long term should only be done in more exceptional cases.
Similarly, if deemed appropriate, a symbol may be undocumented but not immediately deprecated, but that should only be done in exceptional cases for the same reason that symbols would ideally not remain deprecated for long periods of time without being removed. It also has the additional problem that as long as the symbol is not actually deprecated, it is unlikely that existing code will be updated to no longer use it, and if it is undocumented before it is deprecated, then it will be harder for anyone to know how to update their code. So there needs to be a solid reason for deviating from the standard procedure.
If deemed appropriate, the deprecation process can be shorter than ten non-patch releases, but under normal circumstances, it should not be as removing features or symbols too quickly can introduce a maintenance burden on anyone using those features or symbols, and it potentially gives D the image of instability. Two cases where it would make sense for the deprecation period to be shorter would be
- A determination is made that leaving the feature or symbol in the language or library does sufficient harm that it needs to be removed from use as quickly as possible (one example of this would be "accepts-invalid" bug fixes).
- The existence of the current code precludes its own fix or the fix of an equally important issue.
The restriction to at least ten non-patch releases is made with the assumption that releases are made frequently enough that the deprecation period will be approximately two years (the deprecation period that has been used with Phobos for several years now). A change in release scheduling should be accompanied by a modification of this document to bring the number of releases in line with a roughly two year cycle.
In the documentation, a symbol or feature should be marked for removal upon a specific release (e.g. 2.091) rather than a specific date (historically, Phobos has used specific dates rather than releases but has been moving to using releases). Using specific releases is preferable as it allows users to easily determine if upgrading will break their code, whereas release dates tend to only work well when always using the latest release.
At the time of the pull request for deprecation, all code in Phobos, DRuntime, and DMD should be updated to remove use of the affected code (or be deprecated if appropriate, e.g. the tests for a deprecated feature would be deprecated rather than removed). In the case of DRuntime and Phobos, this is currently enforced via makefiles. The maintainers of any projects which are tested on the Project Tester and which are broken by the deprecation should be notified.
At both the time of deprecation and removal, a changelog entry must be made. This changelog entry should have a short motivation for the deprecation (or removal) and should describe which steps can be taken by the user to upgrade their codebase.
In order to facilitate on-schedule deprecations, a comment of the format
@@@DEPRECATED_[version]@@@ should be added to the top of the code which is to be
removed or disabled, where the version number is the version where the feature will
be fully removed from the language or standard library. This comment allows
code to be easily searched before every release to find all planned
deprecations.
All removals or changes to protection attributes of public functions, variables, types, and modules should be accompanied with a deprecation period so as to avoid immediately breaking existing code.
The symbol(s) should be marked using the deprecated keyword with a message
containing the planned release in which the symbol will be removed. A reference
to more information should also be added. E.g. "See the 2.080 changelog for
more details" or "See the function documentation for more details". The
documentation of the symbol(s) should be updated to note the deprecation and
removal plan. The documentation should contain information to help the users of
the symbol(s) transition their code away from the symbol(s).
If the deprecation is occurring because the symbol(s) are being replaced by new
symbols, both the old and the new symbol(s) must be available without a
deprecation message in at least one release to allow users to build their code
without issue on both the stable and master branches. Problems hava risen
in the past when symbols in Phobos were immediately deprecated when
their replacements were added precisely because prominent projects, such as
Dustmite, are tested with both the stable and master branches.
Halfway through the deprecation period (normally after the symbol has been deprecated for five releases), documentation for the symbol should be removed completely while keeping the code itself public until complete removal in order to reduce the likelihood that new code will be written using the deprecated symbol.
If there is no equivalent for the functionality of the removed symbol in the standard library or the runtime, then when the symbol is deprecated, the code should be added to undeaD to allow users to keep their current code when refactoring is not possible.
Under normal circumstances, the removal of a language feature should be accompanied by a deprecation period. "Language features" includes bugs in the current behavior upon which existing code depends, e.g. Issue 10378. Fixing such issues should include a deprecation period for the current behavior and an introduction of the new behavior as the default only at the end of the period. If the D leadership determines that a change is critical enough to be implemented immediately, then of course, it can be implemented immediately, but in general, breaking changes should involve a deprecation period in order to avoid causing serious problems for existing D projects or giving the impression that D is unstable. Whether an "accepts-invalid" bug should include a deprecation period depends on the nature of the bug and the impact of the change, but in general, they should be fixed immediately without a deprecation period.
Deprecations to language features must also update the language deprecations page on dlang.org simultaneously. The deprecation message given by the compiler should contain the planned removal period and/or a reference to more information pertaining to the deprecation.
Warnings should not be used in the deprecation process. Warnings are set as errors in many build systems (including dub), and would therefore prematurely break many user's code. The exception is when a change deprecates a feature which is intended to turn something into a warning. In this case, the code which would trigger the warning must also first go through a deprecation period.
- Remove all uses of the to-be-deprecated feature/symbol from DMD, DRuntime, and Phobos (unless it's in something that is also being deprecated).
- Update the documentation to indicate in which release the feature/symbol will be fully removed as well as give any additional, appropriate information about the deprecation (e.g. what should be used instead). In the case of language changes, this includes updating the language deprecations page.
- Create a changelog entry.
- Deprecate the feature/symbol such that a deprecation message is printed that
has the release that the feature/symbol will be removed in (and thus will be
affected by the
-d,-dw, and-deflags).
- In the case of library symbols, their public documentation should be removed.
- In the case of language features, the spec and the language deprecations page need to be updated for the full removal of the feature.
- Create a changelog entry.
- The feature/symbol should be fully removed.
Copyright (c) 2018 - 2019 by the D Language Foundation
Licensed under Creative Commons Zero 1.0
A change was requested in the specification of the deprecation period. The DIP specified the deprecation period as "at least 10 non-patch releases", which, though not mentioned in the DIP, was based on the pace of DMD releases over a two-year period at the time the DIP was authored. It was suggested to change this to "a minimum of two years" to account for future changes to the pace of DMD releases. The DIP author elected to maintain the existing language with the clarification that it is based on a period of two years and that the DIP should be updated with a new two-year release count to reflect any changes in the pace of major DMD releases.
The DIP specified that deprecated symbols must be annotated with a deprecation message "containing the planned removal period". A request was made to clarify that the meaning of "removal period", i.e. the release in which the symbol is removed from the language vs. the release in which it is removed from the documentation. The author agreed to the change.
Separate suggestions to clarify or simplify the steps of the deprecation process were not addressed by the DIP author.
The observation was made that the meaning of version in @@@DEPRECATED_[version]@@@ is ambiguous -- is it intended to be the version of the compiler in which the feature was deprecated or the version in which the feature is supposed to be removed?
There was one objection to the recommendation that removal of a deprecated symbol from "any module or package wide list of public functions/booktables/cheatsheets", with the explanation that the deprecation notice itself should be enough to discourage use of the deprecated symbol.
A request was made to explicitly outline and describe each stage of the deprecation process in a list in order to make the document easier to read.
The DIP author failed to respond to communication for six months. Rather than mark the DIP as Abandoned, the DIP manager moved
it from Post-Final into the Formal Assessment.
The language maintainers provisionally accepted the DIP on the condition that the following revisions be made:
- the language was too rigid and the language maintainers preferred a set of guidelines rather than hard and fast rules
- the DIP needed to specify that "accepts-invalid" bugs be fixed immediately
- the DIP needed to mention the practice of leaving symbols undocumented long term as some other languages and libraries do
Jonathan Davis agreed to take over the DIP and make the requested revisions. Once they were complete, the language maintainers gave their final approval.