From 305a354fb1f22800634dc802f13072b9a6ebee64 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 3 Aug 2016 10:52:05 -0700 Subject: [PATCH] meta: add explicit deprecation and semver-major policy * Formalizes deprecation policy * Introduces End-of-life deprecation phase to identify code to be removed * Outlines basics of internal vs. public API surface --- COLLABORATOR_GUIDE.md | 206 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 2 deletions(-) diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 7ac8f49ee09ff0..dd5bc4e8005ed2 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -4,6 +4,9 @@ * [Issues and Pull Requests](#issues-and-pull-requests) * [Accepting Modifications](#accepting-modifications) + - [Internal vs. Public API](#internal-vs-public-api) + - [Breaking Changes](#breaking-changes) + - [Deprecations](#deprecations) - [Involving the CTC](#involving-the-ctc) * [Landing Pull Requests](#landing-pull-requests) - [Technical HOWTO](#technical-howto) @@ -84,6 +87,205 @@ All pull requests that modify executable code should be subjected to continuous integration tests on the [project CI server](https://ci.nodejs.org/). +### Internal vs. Public API + +Due to the nature of the JavaScript language, it can often be difficult to +establish a clear distinction between which parts of the Node.js implementation +represent the "public" API Node.js users should assume to be stable and which +are considered part of the "internal" implementation detail of Node.js itself. +A general rule of thumb has been to base the determination off what +functionality is actually *documented* in the official Node.js API +documentation. However, it has been repeatedly demonstrated that either the +documentation does not completely cover implemented behavior or that Node.js +users have come to rely heavily on undocumented aspects of the Node.js +implementation. + +While there are numerous exceptions, the following general rules should be +followed to determine which aspects of the Node.js API are considered +"internal": + +- Any and all functionality exposed via `process.binding(...)` is considered to + be internal and *not* part of the Node.js Public API. +- Any and all functionality implemented in `lib/internal/**/*.js` that is not + re-exported by code in `lib/*.js`, or is not documented as part of the + Node.js Public API, is considered to be internal. +- Any object property or method whose key is a non-exported `Symbol` is + considered to be an internal property. +- Any object property or method whose key begins with the underscore `_` prefix, + and is not documented as part of the Node.js Public API, is considered to be + an internal property. +- Any object, property, method, argument, behavior, or event not documented in + the Node.js documentation is considered to be internal. +- Any native C/C++ APIs/ABIs exported by the Node.js `*.h` header files that + are hidden behind the `NODE_WANT_INTERNALS` flag are considered to be + internal. + +Exception to each of these points can be made if use or behavior of a given +internal API can be demonstrated to be sufficiently relied upon by the Node.js +ecosystem such that any changes would cause too much breakage. The threshhold +for what qualifies as "too much breakage" is to be decided on a case-by-case +basis by the CTC. + +If it is determined that a currently undocumented object, property, method, +argument, or event *should* be documented, then a pull request adding the +documentation is required in order for it to be considered part of the "public" +API. + +Making a determination about whether something *should* be documented can be +difficult and will need to be handled on a case-by-case basis. For instance, if +one documented API cannot be used successfully without the use of a second +*currently undocumented* API, then the second API *should* be documented. If +using an API in a manner currently undocumented achieves a particular useful +result, a decision will need to be made whether or not that falls within the +supported scope of that API; and if it does, it should be documented. + +Breaking changes to internal elements are permitted in semver-patch or +semver-minor commits but Collaborators should take significant care when +making and reviewing such changes. Before landing such commits, an effort +must be made to determine the potential impact of the change in the ecosystem +by analyzing current use and by validating such changes through ecosystem +testing using the [Canary in the Goldmine](https://github.com/nodejs/citgm) +tool. If a change cannot be made without ecosystem breakage, then CTC review is +required before landing the change as anything less than semver-major. + +If a determination is made that a particular internal API (for instance, an +underscore `_` prefixed property) is sufficiently relied upon by the ecosystem +such that any changes may break user code, then serious consideration should be +given to providing an alternative Public API for that functionality before any +breaking changes are made. + +### Breaking Changes + +Backwards-incompatible changes may land on the master branch at any time after +sufficient review by collaborators and approval of at least two CTC members. + +Examples of breaking changes include, but are not necessarily limited to, +removal or redefinition of existing API arguments, changing return values +(except when return values do not currently exist), removing or modifying existing properties on an options argument, adding or removing errors, +changing error messages in any way, altering expected timing of an event (e.g. +moving from sync to async responses or vice versa), and changing the +non-internal side effects of using a particular API. + +With a few notable exceptions outlined below, when backwards incompatible +changes to a *Public* API are necessary, the existing API *must* be deprecated +*first* and the new API either introduced in parallel or added after the next +major Node.js version following the deprecation as a replacement for the +deprecated API. In other words, as a general rule, existing *Public* APIs +*must not* change (in a backwards incompatible way) without a deprecation. + +Exception to this rule is given in the following cases: + +* Adding or removing errors thrown or reported by a Public API; +* Changing error messages; +* Altering the timing and non-internal side effects of the Public API. + +Such changes *must* be handled as semver-major changes but MAY be landed +without a [Deprecation cycle](#deprecation-cycle). + +From time-to-time, in particularly exceptional cases, the CTC may be asked to +consider and approve additional exceptions to this rule. + +Purely additive changes (e.g. adding new events to EventEmitter +implementations, adding new arguments to a method in a way that allows +existing code to continue working without modification, or adding new +properties to an options argument) are handled as semver-minor changes. + +Note that errors thrown, along with behaviors and APIs implemented by +dependencies of Node.js (e.g. those originating from V8) are generally not +under the control of Node.js and therefore *are not directly subject to this +policy*. However, care should still be taken when landing updates to +dependencies when it is known or expected that breaking changes to error +handling may have been made. Additional CI testing may be required. + +#### When breaking changes actually break things + +Breaking changes are difficult primarily because they change the fundamental +assumptions a user of Node.js has when writing their code and can cause +existing code to stop functioning as expected -- costing developers and users +time and energy to fix. + +Because breaking (semver-major) changes are permitted to land in master at any +time, it should be *understood and expected* that at least some subset of the +user ecosystem *may* be adversely affected *in the short term* when attempting +to build and use Node.js directly from master. This potential instability is +precisely why Node.js offers distinct Current and LTS release streams that +offer explicit stability guarantees. + +Specifically: + +* Breaking changes should *never* land in Current or LTS except when: + * Resolving critical security issues. + * Fixing a critical bug (e.g. fixing a memory leak) requires a breaking + change. + * There is CTC consensus that the change is required. +* If a breaking commit does accidentally land in a Current or LTS branch, an + attempt to fix the issue will be made before the next release; If no fix is + provided then the commit will be reverted. + +When any change is landed in master, and it is determined that the such +changes *do* break existing code, a decision may be made to revert those +changes either temporarily or permanently. However, the decision to revert or +not can often be based on many complex factors that are not easily codified. It +is also possible that the breaking commit can be labeled retroactively as a +semver-major change that will not be backported to Current or LTS branches. + +### Deprecations + +Deprecation refers to the identification of Public APIs that should no longer +be used and that may be removed or modified in non-backwards compatible ways in +a future major release of Node.js. Deprecation *may* be used with internal APIs +if there is expected impact on the user community. + +Node.js uses three fundamental Deprecation levels: + +* *Documentation-Only Deprecation* refers to elements of the Public API that are + being staged for deprecation in a future Node.js major release. An explicit + notice indicating the deprecated status is added to the API documentation + *but no functional changes are implemented in the code*. There will be no + runtime deprecation warning emitted for such deprecations. + +* *Runtime Deprecation* refers to the use of process warnings emitted at + runtime the first time that a deprecated API is used. A command-line + switch can be used to escalate such warnings into runtime errors that will + cause the Node.js process to exit. As with Documentation-Only Deprecation, + the documentation for the API must be updated to clearly indicate the + deprecated status. + +* *End-of-life* refers to APIs that have gone through Runtime Deprecation and + are ready to be removed from Node.js entirely. + +Documentation-Only Deprecations *may* be handled as semver-minor or +semver-major changes. Such deprecations have no impact on the successful +operation of running code and therefore should not be viewed as breaking +changes. + +Runtime Deprecations and End-of-life APIs (internal or public) *must* be +handled as semver-major changes unless there is CTC consensus to land the +deprecation as a semver-minor. + +All Documentation-Only and Runtime deprecations will be assigned a unique +identifier that can be used to persistently refer to the deprecation in +documentation, emitted process warnings, or errors thrown. Documentation for +these identifiers will be included in the Node.js API documentation and will +be immutable once assigned. Even if End-of-Life code is removed from Node.js, +the documentation for the assigned deprecation identifier must remain in the +Node.js API documentation. + + +A "Deprecation cycle" is one full Node.js major release during which an API +has been in one of the three Deprecation levels. (Note that Documentation-Only +Deprecations may land in a Node.js minor release but must not be upgraded to +a Runtime Deprecation until the next major release.) + +No API can be moved to End-of-life without first having gone through a +Runtime Deprecation cycle. + +A best effort will be made to communicate pending deprecations and associated +mitigations with the ecosystem as soon as possible (preferably *before* the pull +request adding the deprecation lands in master). All deprecations included in +a Node.js release should be listed prominently in the "Notable Changes" section +of the release notes. + ### Involving the CTC Collaborators may opt to elevate pull requests or issues to the CTC for @@ -291,7 +493,7 @@ You can find more information [in the full LTS plan](https://github.com/nodejs/l #### How does LTS work? -Once a stable branch enters LTS, changes in that branch are limited to bug +Once a Current branch enters LTS, changes in that branch are limited to bug fixes, security updates, possible npm updates, documentation updates, and certain performance improvements that can be demonstrated to not break existing applications. Semver-minor changes are only permitted if required for bug fixes @@ -299,7 +501,7 @@ and then only on a case-by-case basis with LTS WG and possibly Core Technical Committee (CTC) review. Semver-major changes are permitted only if required for security related fixes. -Once a stable branch moves into Maintenance mode, only **critical** bugs, +Once a Current branch moves into Maintenance mode, only **critical** bugs, **critical** security fixes, and documentation updates will be permitted. #### Landing semver-minor commits in LTS