Skip to content

Commit

Permalink
meta: add explicit deprecation and semver-major policy
Browse files Browse the repository at this point in the history
* Formalizes deprecation policy
* Introduces End-of-life deprecation phase to identify code to be removed
* Outlines basics of internal vs. public API surface

PR-URL: #7964
Reviewed-By: Evan Lucas <[email protected]>
Reviewed-By: Sam Roberts <[email protected]>
Reviewed-By: Trevor Norris <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
  • Loading branch information
jasnell authored and MylesBorins committed Mar 9, 2017
1 parent 898276b commit 8b5dd35
Showing 1 changed file with 204 additions and 2 deletions.
206 changes: 204 additions & 2 deletions COLLABORATOR_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 id="deprecation-cycle"></a>
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
Expand Down Expand Up @@ -291,15 +493,15 @@ 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
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
Expand Down

0 comments on commit 8b5dd35

Please sign in to comment.