Skip to content

Commit 533d2bf

Browse files
jasnellMylesBorins
authored andcommitted
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 PR-URL: #7964 Reviewed-By: Evan Lucas <evanlucas@me.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 2016aa4 commit 533d2bf

File tree

1 file changed

+204
-2
lines changed

1 file changed

+204
-2
lines changed

COLLABORATOR_GUIDE.md

+204-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
* [Issues and Pull Requests](#issues-and-pull-requests)
66
* [Accepting Modifications](#accepting-modifications)
7+
- [Internal vs. Public API](#internal-vs-public-api)
8+
- [Breaking Changes](#breaking-changes)
9+
- [Deprecations](#deprecations)
710
- [Involving the CTC](#involving-the-ctc)
811
* [Landing Pull Requests](#landing-pull-requests)
912
- [Technical HOWTO](#technical-howto)
@@ -81,6 +84,205 @@ All pull requests that modify executable code should be subjected to
8184
continuous integration tests on the
8285
[project CI server](https://ci.nodejs.org/).
8386

87+
### Internal vs. Public API
88+
89+
Due to the nature of the JavaScript language, it can often be difficult to
90+
establish a clear distinction between which parts of the Node.js implementation
91+
represent the "public" API Node.js users should assume to be stable and which
92+
are considered part of the "internal" implementation detail of Node.js itself.
93+
A general rule of thumb has been to base the determination off what
94+
functionality is actually *documented* in the official Node.js API
95+
documentation. However, it has been repeatedly demonstrated that either the
96+
documentation does not completely cover implemented behavior or that Node.js
97+
users have come to rely heavily on undocumented aspects of the Node.js
98+
implementation.
99+
100+
While there are numerous exceptions, the following general rules should be
101+
followed to determine which aspects of the Node.js API are considered
102+
"internal":
103+
104+
- Any and all functionality exposed via `process.binding(...)` is considered to
105+
be internal and *not* part of the Node.js Public API.
106+
- Any and all functionality implemented in `lib/internal/**/*.js` that is not
107+
re-exported by code in `lib/*.js`, or is not documented as part of the
108+
Node.js Public API, is considered to be internal.
109+
- Any object property or method whose key is a non-exported `Symbol` is
110+
considered to be an internal property.
111+
- Any object property or method whose key begins with the underscore `_` prefix,
112+
and is not documented as part of the Node.js Public API, is considered to be
113+
an internal property.
114+
- Any object, property, method, argument, behavior, or event not documented in
115+
the Node.js documentation is considered to be internal.
116+
- Any native C/C++ APIs/ABIs exported by the Node.js `*.h` header files that
117+
are hidden behind the `NODE_WANT_INTERNALS` flag are considered to be
118+
internal.
119+
120+
Exception to each of these points can be made if use or behavior of a given
121+
internal API can be demonstrated to be sufficiently relied upon by the Node.js
122+
ecosystem such that any changes would cause too much breakage. The threshhold
123+
for what qualifies as "too much breakage" is to be decided on a case-by-case
124+
basis by the CTC.
125+
126+
If it is determined that a currently undocumented object, property, method,
127+
argument, or event *should* be documented, then a pull request adding the
128+
documentation is required in order for it to be considered part of the "public"
129+
API.
130+
131+
Making a determination about whether something *should* be documented can be
132+
difficult and will need to be handled on a case-by-case basis. For instance, if
133+
one documented API cannot be used successfully without the use of a second
134+
*currently undocumented* API, then the second API *should* be documented. If
135+
using an API in a manner currently undocumented achieves a particular useful
136+
result, a decision will need to be made whether or not that falls within the
137+
supported scope of that API; and if it does, it should be documented.
138+
139+
Breaking changes to internal elements are permitted in semver-patch or
140+
semver-minor commits but Collaborators should take significant care when
141+
making and reviewing such changes. Before landing such commits, an effort
142+
must be made to determine the potential impact of the change in the ecosystem
143+
by analyzing current use and by validating such changes through ecosystem
144+
testing using the [Canary in the Goldmine](https://github.com/nodejs/citgm)
145+
tool. If a change cannot be made without ecosystem breakage, then CTC review is
146+
required before landing the change as anything less than semver-major.
147+
148+
If a determination is made that a particular internal API (for instance, an
149+
underscore `_` prefixed property) is sufficiently relied upon by the ecosystem
150+
such that any changes may break user code, then serious consideration should be
151+
given to providing an alternative Public API for that functionality before any
152+
breaking changes are made.
153+
154+
### Breaking Changes
155+
156+
Backwards-incompatible changes may land on the master branch at any time after
157+
sufficient review by collaborators and approval of at least two CTC members.
158+
159+
Examples of breaking changes include, but are not necessarily limited to,
160+
removal or redefinition of existing API arguments, changing return values
161+
(except when return values do not currently exist), removing or modifying existing properties on an options argument, adding or removing errors,
162+
changing error messages in any way, altering expected timing of an event (e.g.
163+
moving from sync to async responses or vice versa), and changing the
164+
non-internal side effects of using a particular API.
165+
166+
With a few notable exceptions outlined below, when backwards incompatible
167+
changes to a *Public* API are necessary, the existing API *must* be deprecated
168+
*first* and the new API either introduced in parallel or added after the next
169+
major Node.js version following the deprecation as a replacement for the
170+
deprecated API. In other words, as a general rule, existing *Public* APIs
171+
*must not* change (in a backwards incompatible way) without a deprecation.
172+
173+
Exception to this rule is given in the following cases:
174+
175+
* Adding or removing errors thrown or reported by a Public API;
176+
* Changing error messages;
177+
* Altering the timing and non-internal side effects of the Public API.
178+
179+
Such changes *must* be handled as semver-major changes but MAY be landed
180+
without a [Deprecation cycle](#deprecation-cycle).
181+
182+
From time-to-time, in particularly exceptional cases, the CTC may be asked to
183+
consider and approve additional exceptions to this rule.
184+
185+
Purely additive changes (e.g. adding new events to EventEmitter
186+
implementations, adding new arguments to a method in a way that allows
187+
existing code to continue working without modification, or adding new
188+
properties to an options argument) are handled as semver-minor changes.
189+
190+
Note that errors thrown, along with behaviors and APIs implemented by
191+
dependencies of Node.js (e.g. those originating from V8) are generally not
192+
under the control of Node.js and therefore *are not directly subject to this
193+
policy*. However, care should still be taken when landing updates to
194+
dependencies when it is known or expected that breaking changes to error
195+
handling may have been made. Additional CI testing may be required.
196+
197+
#### When breaking changes actually break things
198+
199+
Breaking changes are difficult primarily because they change the fundamental
200+
assumptions a user of Node.js has when writing their code and can cause
201+
existing code to stop functioning as expected -- costing developers and users
202+
time and energy to fix.
203+
204+
Because breaking (semver-major) changes are permitted to land in master at any
205+
time, it should be *understood and expected* that at least some subset of the
206+
user ecosystem *may* be adversely affected *in the short term* when attempting
207+
to build and use Node.js directly from master. This potential instability is
208+
precisely why Node.js offers distinct Current and LTS release streams that
209+
offer explicit stability guarantees.
210+
211+
Specifically:
212+
213+
* Breaking changes should *never* land in Current or LTS except when:
214+
* Resolving critical security issues.
215+
* Fixing a critical bug (e.g. fixing a memory leak) requires a breaking
216+
change.
217+
* There is CTC consensus that the change is required.
218+
* If a breaking commit does accidentally land in a Current or LTS branch, an
219+
attempt to fix the issue will be made before the next release; If no fix is
220+
provided then the commit will be reverted.
221+
222+
When any change is landed in master, and it is determined that the such
223+
changes *do* break existing code, a decision may be made to revert those
224+
changes either temporarily or permanently. However, the decision to revert or
225+
not can often be based on many complex factors that are not easily codified. It
226+
is also possible that the breaking commit can be labeled retroactively as a
227+
semver-major change that will not be backported to Current or LTS branches.
228+
229+
### Deprecations
230+
231+
Deprecation refers to the identification of Public APIs that should no longer
232+
be used and that may be removed or modified in non-backwards compatible ways in
233+
a future major release of Node.js. Deprecation *may* be used with internal APIs
234+
if there is expected impact on the user community.
235+
236+
Node.js uses three fundamental Deprecation levels:
237+
238+
* *Documentation-Only Deprecation* refers to elements of the Public API that are
239+
being staged for deprecation in a future Node.js major release. An explicit
240+
notice indicating the deprecated status is added to the API documentation
241+
*but no functional changes are implemented in the code*. There will be no
242+
runtime deprecation warning emitted for such deprecations.
243+
244+
* *Runtime Deprecation* refers to the use of process warnings emitted at
245+
runtime the first time that a deprecated API is used. A command-line
246+
switch can be used to escalate such warnings into runtime errors that will
247+
cause the Node.js process to exit. As with Documentation-Only Deprecation,
248+
the documentation for the API must be updated to clearly indicate the
249+
deprecated status.
250+
251+
* *End-of-life* refers to APIs that have gone through Runtime Deprecation and
252+
are ready to be removed from Node.js entirely.
253+
254+
Documentation-Only Deprecations *may* be handled as semver-minor or
255+
semver-major changes. Such deprecations have no impact on the successful
256+
operation of running code and therefore should not be viewed as breaking
257+
changes.
258+
259+
Runtime Deprecations and End-of-life APIs (internal or public) *must* be
260+
handled as semver-major changes unless there is CTC consensus to land the
261+
deprecation as a semver-minor.
262+
263+
All Documentation-Only and Runtime deprecations will be assigned a unique
264+
identifier that can be used to persistently refer to the deprecation in
265+
documentation, emitted process warnings, or errors thrown. Documentation for
266+
these identifiers will be included in the Node.js API documentation and will
267+
be immutable once assigned. Even if End-of-Life code is removed from Node.js,
268+
the documentation for the assigned deprecation identifier must remain in the
269+
Node.js API documentation.
270+
271+
<a id="deprecation-cycle"></a>
272+
A "Deprecation cycle" is one full Node.js major release during which an API
273+
has been in one of the three Deprecation levels. (Note that Documentation-Only
274+
Deprecations may land in a Node.js minor release but must not be upgraded to
275+
a Runtime Deprecation until the next major release.)
276+
277+
No API can be moved to End-of-life without first having gone through a
278+
Runtime Deprecation cycle.
279+
280+
A best effort will be made to communicate pending deprecations and associated
281+
mitigations with the ecosystem as soon as possible (preferably *before* the pull
282+
request adding the deprecation lands in master). All deprecations included in
283+
a Node.js release should be listed prominently in the "Notable Changes" section
284+
of the release notes.
285+
84286
### Involving the CTC
85287

86288
Collaborators may opt to elevate pull requests or issues to the CTC for
@@ -253,15 +455,15 @@ You can find more information [in the full LTS plan](https://github.com/nodejs/l
253455

254456
#### How does LTS work?
255457

256-
Once a stable branch enters LTS, changes in that branch are limited to bug
458+
Once a Current branch enters LTS, changes in that branch are limited to bug
257459
fixes, security updates, possible npm updates, documentation updates, and
258460
certain performance improvements that can be demonstrated to not break existing
259461
applications. Semver-minor changes are only permitted if required for bug fixes
260462
and then only on a case-by-case basis with LTS WG and possibly Core Technical
261463
Committee (CTC) review. Semver-major changes are permitted only if required for
262464
security related fixes.
263465

264-
Once a stable branch moves into Maintenance mode, only **critical** bugs,
466+
Once a Current branch moves into Maintenance mode, only **critical** bugs,
265467
**critical** security fixes, and documentation updates will be permitted.
266468

267469
#### Landing semver-minor commits in LTS

0 commit comments

Comments
 (0)