From a7eaac7f57ac1b0c4d1897a1beae0cafd3811790 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 12 Jul 2021 18:00:18 +0200 Subject: [PATCH] document some std library evolution policies In wanting to improve the standard library, it's helpful to have a set of principles and guidelines to lean back on, that show how to introduce such improvements while at the same time considering existing users of the language. A key idea behind documentation of this sort is that it should highlight a path forwards in making changes rather than trying to prevent them, and although the current snippet does include some language for what one shouldn't do, it also shows a few options for what one can do. This is a riff on https://github.com/nim-lang/Nim/pull/18468 mainly based on what helps enable to the use of Nim productively in environments and teams where the priority and focus is not always on the tool (Nim in this case) but rather on the codebase itself, and its use by end users. We use similar guidance documentation to help coordinate the code of the teams using Nim in https://status-im.github.io/nim-style-guide/ where it is applied not as law, but rather as recommendations for the default approach that should be considered first. --- doc/contributing.rst | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/doc/contributing.rst b/doc/contributing.rst index 5a31d85bdec80..64a168c834240 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -671,3 +671,48 @@ Conventions Furthermore, module names should use `snake_case` and not use capital letters, which cause issues when going from an OS without case sensitivity to an OS with it. + +Introducing change +------------------ + +Nim is used for mission critical applications which depend on behaviours that +are not covered by the test suite. As such, it's important that changes to the +*stable* parts of the standard library are made avoiding changing the existing +behaviors, even when the test suite continues to pass. Above all else, additive +approaches that don't change existing behaviors should be preferred. + +Examples of breaking changes include (but are not limited to): + +* renaming functions and modules, or moving things +* raising exceptions of a new type, compared to what's currently being raised +* changing behavior of existing functions + * this includes inputs around edge cases or undocumented behaviors +* adding overloads or generic `proc`:s, in particular when an unconstrained generic implementation exists already + * this may cause code to behave differently depending only on which modules are imported - common examples include `==` and `hash` +* hiding the old behavior behind a `-d:nimLegacy` flag + * legacy flags are suitable for use between major, breaking release of the langauge or non-breaking changes that nontheless are deemed sensitive + +Examples of changes that are considered non-breaking include: + +* creating a new module, or adding a function with a new name + * this may create compile-time issues due to name conflicts +* changing the behavior of a function where previously it was crashing, raising `Defect` or giving an incorrect result +* addressing issues whose invalid or undefined behavior can be made a compile-time error + +Some behaviors in the standard library are inconsistent or unfortunate. Even so, code may have come to depend on the quirks, and it's often not possible to tell. When encountering such behaviors, consider the following approaches instead: + +* document the quirk in manual and tutorials to raise awareness +* create a separate, stand-alone Nimble package and deprecate the existing function or module + * this allows the API to evolve at its own pace - in stand-alone packages, it's also easier to introduce breaking changes since they are not bundled with other changes + * the deprecation message should explain why the existing behavior is problematic +* deprectate the existing function or module, and create a new implementation in a separate module + * this approach is suitable for functionality closely tied to the language +* introduce the new behavior behind a `-d:nimEnable` flag + * this approach is suitable for experimental changes and changes which will become default in a future breaking release of Nim, allowing early adopters to experiment with the new behavior + +Introducing breaking changes in the standard library, no matter how well intentioned, creates long-term problems for the community, in particular those looking to protomote reusable Nim code in libraries: + +* in the Nim distribution, critical security and bugfixes, language changes and community improvements are bundled in a single distribution - it is difficult to make partial upgrades with only benign changes + * when one library depends on a legacy behavior, it can no longer be used together with another library that does not, breaking all downstream applications - the standard library is unique in that it sits at the root of _all_ dependency trees + +These guidelines apply to released versions of the language - `devel` can always be expected to change.