From e009502895b26674a869051582a29233c28eb5ad Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Fri, 6 Oct 2023 14:33:16 -0700 Subject: [PATCH] docs: document a breaking changes policy (#1460) This largely covers how to introduce a breaking change. It also attempts to clarify what is or isn't a breaking change. Closes #1424 Work towards #1361 --- .github/PULL_REQUEST_TEMPLATE.md | 1 + CONTRIBUTING.md | 76 +++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 66903df0c2..c347266583 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,6 +4,7 @@ PR Instructions/requirements * Update `CHANGELOG.md` as applicable * Breaking changes include "!" after the type and a "BREAKING CHANGES:" section at the bottom. + See CONTRIBUTING.md for our breaking changes process. * Body text describes: * Why this change is being made, briefly. * Before and after behavior, as applicable diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 54ecfb01e5..bf3a496158 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -128,7 +128,9 @@ BREAKING CHANGE: ``` Where `(scope)` is optional, and `!` is only required if there is a breaking change. -If a breaking change is introduced, then `BREAKING CHANGE:` is required. +If a breaking change is introduced, then `BREAKING CHANGE:` is required; see +the [Breaking Changes](#breaking-changes) section for how to introduce breaking +changes. Common `type`s: @@ -184,6 +186,78 @@ Issues should be triaged as follows: functionality, should also be filed in this repository but without the `core-rules` label. +## Breaking Changes + +Breaking changes are generally permitted, but we follow a 3-step process for +introducing them. The intent behind this process is to balance the difficulty of +version upgrades for users, maintaining multiple code paths, and being able to +introduce modern functionality. + +The general process is: + +1. In version `N`, introduce the new behavior, but it must be disabled by + default. Users can opt into the new functionality when they upgrade to + version `N`, which lets them try it and verify functionality. +2. In version `N+1`, the new behavior can be enabled by default. Users can + opt out if necessary, but doing so causes a warning to be issued. +3. In version `N+2`, the new behavior is always enabled and cannot be opted out + of. The API for the control mechanism can be removed in this release. + +Note that the `+1` and `+2` releases are just examples; the steps are not +required to happen in immedially subsequent releases. + + +### How to control breaking changes + +The details of the control mechanism will depend on the situation. Below is +a summary of some different options. + +* Environment variables are best for repository rule behavior. Environment + variables can be propagated to rules and macros using the generated + `@rules_python_internal//:config.bzl` file. +* Attributes are applicable to macros and regular rules, especially when the + behavior is likely to vary on a per-target basis. +* [User defined build settings](https://bazel.build/extending/config#user-defined-build-settings) + (aka custom build flags) are applicable for rules when the behavior change + generally wouldn't vary on a per-target basis. They also have the benefit that + an entire code base can have them easily enabled by a bazel command line flag. +* Allowlists allow a project to centrally control if something is + enabled/disabled. Under the hood, they are basically a specialized custom + build flag. + +Note that attributes and flags can seamlessly interoperate by having the default +controlled by a flag, and an attribute can override the flag setting. This +allows a project to enable the new behavior by default while they work to fix +problematic cases to prepare for the next upgrade. + +### What is considered a breaking change? + +Precisely defining what constitutes a breaking change is hard because it's +easy for _someone, somewhere_ to depend on _some_ observable behavior, despite +our best efforts to thoroughly document what is or isn't supported and hiding +any internal details. + +In general, something is considered a breaking change when it changes the +direct behavior of a supported public API. Simply being able to observe a +behavior change doesn't necessarily mean it's a breaking change. + +Long standing undocumented behavior is a large grey area and really depends on +how load-bearing it has become and what sort of reasonable expectation of +behavior there is. + +Here's some examples of what would or wouldn't be considered a breaking change. + +Breaking changes: + * Renaming an function argument for public functions. + * Enforcing stricter validation than was previously required when there's a + sensible reason users would run afoul of it. + * Changing the name of a public rule. + +Not breaking changes: + * Upgrading dependencies + * Changing internal details, such as renaming an internal file. + * Changing a rule to a macro. + ## FAQ ### Installation errors when during `git commit`