diff --git a/docs/src/developing/backwards-compatibility.md b/docs/src/developing/backwards-compatibility.md index 4a3c60b8e129bd..646b3508cf7d36 100644 --- a/docs/src/developing/backwards-compatibility.md +++ b/docs/src/developing/backwards-compatibility.md @@ -1,5 +1,5 @@ --- -title: Backward Compatibility Policy +title: Backwards Compatibility Policy --- As the Solana developer ecosystem grows, so does the need for clear expectations around @@ -11,16 +11,16 @@ and so this document attempts to clarify and codify the process for new releases ### Expectations - Solana software releases include APIs, SDKs, and CLI tooling (with a few [exceptions](#exceptions)). -- Solana software releases follow semantic versioning, more details below. -- Software for a `MINOR` version release will be compatible across all software on the - same `MAJOR` version. +- Solana software releases *do not always* follow semantic versioning, more details below. +- Software for a `MINOR` version release will be compatible with the previous + `MINOR` releases, and following `MINOR` releases, for at least one year. ### Deprecation Process 1. In any `PATCH` or `MINOR` release, a feature, API, endpoint, etc. could be marked as deprecated. -2. According to code upgrade difficulty, some features will be remain deprecated for a few release +2. According to code upgrade difficulty, some features will remain deprecated for a few release cycles. -3. In a future `MAJOR` release, deprecated features will be removed in an incompatible way. +3. At least one year later, deprecated features may be removed in an incompatible way. ### Release Cadence @@ -30,15 +30,18 @@ updates of a particular `MINOR` version release. #### Release Channels -- `edge` software that contains cutting-edge features with no backward compatibility policy +- `edge` software that contains cutting-edge features with no backwards compatibility policy - `beta` software that runs on the Solana Testnet cluster - `stable` software that run on the Solana Mainnet Beta and Devnet clusters #### Major Releases (x.0.0) -`MAJOR` version releases (e.g. 2.0.0) may contain breaking changes and removal of previously -deprecated features. Client SDKs and tooling will begin using new features and endpoints -that were enabled in the previous `MAJOR` version. +RPC `MAJOR` version releases (e.g. 2.0.0) may contain breaking changes and removal +of previously deprecated features. + +We do not expect the Rust SDK `MAJOR` version to be changed. Any breaking changes +will be done though the deprecation process described in +[Why not just use SemVer](#why-not-just-use-semver) in a `MINOR` version. #### Minor Releases (1.x.0) @@ -48,6 +51,12 @@ on the testnet, `MINOR` versions are considered to be in the `beta` release chan those changes have been patched as needed and proven to be reliable, the `MINOR` version will be upgraded to the `stable` release channel and deployed to the Mainnet Beta cluster. +The Rust SDK may contain breaking changes and removal of previously deprecated features +in a `MINOR` release. Every `MINOR` release will be compatible with at least one year of +preceding `MINOR` releases, and at least one year of future `MINOR` releases. + +More details in [Why not just use SemVer](#why-not-just-use-semver). + #### Patch Releases (1.0.x) Low risk features, non-breaking changes, and security and bug fixes are shipped as part @@ -87,11 +96,12 @@ Patch releases: Minor releases: - New APIs +- Removal of deprecated APIs, subject to deprecation delay +- Backwards incompatible behavior changes, subject to deprecation delay -Major releases +Major releases: -- Removal of deprecated APIs -- Backwards incompatible behavior changes +- Are not projected to happen ever. More details in [Why not just use SemVer](#why-not-just-use-semver). ### CLI Tools @@ -155,3 +165,81 @@ circumvented in order to rapidly deploy a fix, depending on the severity of the CLI tooling json output (`output --json`) compatibility will be preserved; however, output directed for a human reader is subject to change. This includes output as well as potential help, warning, or error messages. + +### Why not just use SemVer + +The Solana Rust SDK crates do *not* follow semantic versioning (SemVer) for +breaking changes. + +#### SemVer in Rust + +Under SemVer, breaking changes, such as removal of functions or types, only happens +in a new major version. + +In many situations, this is useful -- it is undesirable to pick up a breaking +change accidentally. The default dependency declaration in Cargo follows SemVer. +It assumes that all minor versions are compatible, and will automatically update +to the newest minor version available. + +For example, if you declare `solana-program = "1.10"`, Cargo can pull in version +`1.16`, since it assumes that all `1.X.Y` releases are compatible. + +If, however, Cargo deems that two versions of a package are incompatible, it will +treat them as two completely separate packages. + +Packages can be deemed incompatible for many reasons, but the most common situation +under the default declaration format (SemVer) is due to different major versions. + +If a package ends up with two or more instances of `solana-program`, +[bad things will happen](https://doc.rust-lang.org/cargo/reference/resolver.html#version-incompatibility-hazards), +either at compile-time or runtime, if the conflicting types in the public APIs +are ever used together. + +Because of these issues, developers should only want one instance of `solana-program` +in their project. This includes *all* direct and indirect dependencies: they should +all use the same `solana-program`. + +If developers all use the default versioning declaration with major version `1`, +then we can ensure that only one version of `solana-program` exists in the resolution. + +The specifics of the Cargo resolver are beyond the scope of this document. +You can find more information about the Cargo resolver in +[The Cargo Book](https://doc.rust-lang.org/cargo/reference/resolver.html). + +#### So what's your solution? + +We would like all releases to be 100% backwards compatible, allowing everyone +to upgrade at any time with no effort. This model, however, would put overly +strict constraints on the development of the Rust SDK crates, considering we cannot +in practice increase the major version. + +So we settle on a compromise. We guarantee full compatibility, but only for a +certain period of time. First we deprecate APIs, and only remove or break the +API after at least one year has passed. + +We are in the process of setting up tests that verify compatibility guarantees to match +our policy. But, as any test, it may have gaps, and is subject to engineering time +invested in the test coverage. + +Should we break backwards compatibility, we will do whatever is necessary to restore +functionality, within reason. + +This model has precedents in many other platforms. +[Rust editions](https://blog.rust-lang.org/2021/05/11/edition-2021.html#what-is-an-edition), +can introduce backwards-incompatible changes. + +And there are even more examples in +[Why Semantic Versioning Isn't](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e): + +> Node doesn't follow SemVer, Rails doesn't do it, Python doesn't do it, Ruby doesn't do it, even npm doesn't follow SemVer. + +The Solana SDK is closer to a platform than a library, so it makes more sense to +follow other platforms. + +Additionally, the +[Minimum Supported Rust Version (MSRV) Policies](https://github.com/rust-lang/api-guidelines/discussions/231) +suggest that updating MSRV is *not* a SemVer breaking change, even though it +effectively forces users to upgrade their compiler just to update a crate. + +By ensuring that projects will never immediately break, and giving developers +one year to update, this model will keep stability while avoiding stagnation.