Skip to content

Commit 1ae783b

Browse files
committed
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 <[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]>
1 parent eaab99e commit 1ae783b

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)
@@ -84,6 +87,205 @@ All pull requests that modify executable code should be subjected to
8487
continuous integration tests on the
8588
[project CI server](https://ci.nodejs.org/).
8689

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

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

292494
#### How does LTS work?
293495

294-
Once a stable branch enters LTS, changes in that branch are limited to bug
496+
Once a Current branch enters LTS, changes in that branch are limited to bug
295497
fixes, security updates, possible npm updates, documentation updates, and
296498
certain performance improvements that can be demonstrated to not break existing
297499
applications. Semver-minor changes are only permitted if required for bug fixes
298500
and then only on a case-by-case basis with LTS WG and possibly Core Technical
299501
Committee (CTC) review. Semver-major changes are permitted only if required for
300502
security related fixes.
301503

302-
Once a stable branch moves into Maintenance mode, only **critical** bugs,
504+
Once a Current branch moves into Maintenance mode, only **critical** bugs,
303505
**critical** security fixes, and documentation updates will be permitted.
304506

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

0 commit comments

Comments
 (0)