Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions docs/deprecation-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Deprecation policy

## Preamble
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Preamble

No section heading is needed for this introductory section. "Preamble" is a bit grandiose for this document IMHO.


The Content Authenticity Initiative SDKs are evolving projects. Prior to their 1.0 release, the APIs may change as we refine our design. That said, we are committed to making those changes in a transparent, predictable way so that developers can plan accordingly.

This policy applies to the Rust SDK and all language bindings (JavaScript, Node.js, C, C++, Swift, Kotlin, and Python).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This policy applies to the Rust SDK and all language bindings (JavaScript, Node.js, C, C++, Swift, Kotlin, and Python).
This policy applies to the Rust library and all language bindings (JavaScript, Node.js, C, C++, Swift, Kotlin, and Python).

Prefer to refer to each repo as a "library" vs. the entire set as the "SDK". This is the terminology used in the docs.


## Versioning and stability guarantees

We follow [Semantic Versioning (SemVer)](https://semver.org/). Version 1.0.0 will define the public API, and after that release, the way version numbers are incremented will be dependent on how the public API changes: patch for backward-compatible bug fixes, minor for backward-compatible additions, and major for incompatible changes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
We follow [Semantic Versioning (SemVer)](https://semver.org/). Version 1.0.0 will define the public API, and after that release, the way version numbers are incremented will be dependent on how the public API changes: patch for backward-compatible bug fixes, minor for backward-compatible additions, and major for incompatible changes.
We follow [Semantic Versioning (SemVer)](https://semver.org/). Version 1.0.0 defines the public API, and subsequent version numbers are based on how the public API changes in the release:
- **Patch** release for backward-compatible bug fixes.
- **Minor** release for backward-compatible additions.
- **Major** release for incompatible changes.

Small wording change so that the doc will not be time-specific: "1.0.0 will define..." implies the release is in future--which it is now, but won't be later. And formatting change to make it easier to read.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll partially accept this. IMHO we should retain the future-tense wording and change the language when we actually reach 1.0.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change the language when we actually reach 1.0.

Just don't forget that part!


**Before 1.0:** Major version zero (`0.y.z`) is for initial development — anything may change at any time, and the public API should not be considered stable. In the Rust/Cargo ecosystem, this means that a change from `0.2.3` to `0.3.0` may include incompatible API changes. We will, however, make a good-faith effort to follow the deprecation process below even before 1.0, so that users have advance warning before breakage occurs.

**After 1.0:** Breaking changes will only ship in major version bumps. Before completely removing functionality in a new major release, there should be at least one minor release that contains the deprecation so that users can smoothly transition to the new API. We will publish and retain historical documentation for at least each minor point release.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**After 1.0:** Breaking changes will only ship in major version bumps. Before completely removing functionality in a new major release, there should be at least one minor release that contains the deprecation so that users can smoothly transition to the new API. We will publish and retain historical documentation for at least each minor point release.
**After 1.0:** Breaking changes will only ship in major version increments. Before completely removing functionality in a new major release, there will be at least one minor release that contains the deprecation so that API consumers can smoothly transition to the new API. We will publish and retain historical documentation for at least each minor point release.

Small wording changes:

  • "should" -> "will" (assumig that this will always be true. If not, then we should specify exceptions)
  • "users" -> "API consumers" to clarify that we are not talking about end-users.


## What counts as a breaking change

Not all changes are equal. Breaking changes are assumed to be major changes, but not all breaking changes are major. The goal is that the same code should be able to run against different minor revisions, and minor changes should require at most a few local annotations. (This document is Rust-specific; we will treat other languages as closely to this list as is feasible.)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. Do you mean "Breaking changes are assumed to be major changes, but not all major changes are breaking changes." ?

If so, maybe reword as "All breaking changes are major changes, but not all major changes are breaking."

If not, then what exactly does this mean? Most breaking changes are major, but in some cases they are not? When are breaking changes NOT major??

Please clarify.


Changes we consider **breaking** (requiring a major version bump post-1.0):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Changes we consider **breaking** (requiring a major version bump post-1.0):
Breaking changes that require a major version change (post-1.0) include:


- Moving a public type, function, method, trait, or constant from one parent module to another
- Removing or renaming a public type, function, method, trait, or constant
- Changing the signature of a public function (parameter types, return types, or generics) except to the extent such changes are generally considered non-breaking (e.g. changing a `&mut Type` to `&Type`)
- Changing the behavior of a public API in a way that violates previously documented contracts
- Removing or renaming public enum variants or struct fields
- Adding public enum variants or struct fields (unless `#[non_exhaustive]` was applied)
- Breaking changes to upstream or third-party libraries to the extent that those APIs are re-published by our library and thus break our own API compatibility
- Any other change flagged by `cargo-server-checks` (or an equivalent tool for any other language) as breaking compatibility

Changes we consider **non-breaking** (minor or patch):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Changes we consider **non-breaking** (minor or patch):
Non-breaking changes that require a minor or patch version change:


- Adding new public items (types, functions, trait impls)
- Deprecating a public item without removing it
- Bug fixes that restore documented behavior

## The deprecation process

When we decide to remove or replace part of the public API, we follow a three-stage process:

### Stage 1: Deprecation notice (minor release)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Stage 1: Deprecation notice (minor release)
### Stage 1: Deprecation notice (minor release)
The initial stage provides advance notice of the deprecation:


- The item is marked deprecated in source code using the appropriate language mechanism. (See "Language-specific deprecation annotations" below.)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a numbered list, because the steps are performed in the specified order.

- The deprecation message includes: what is deprecated, why, what to use instead, and the earliest planned removal timeline. (See stage 2.)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is practical or necessary to add the earliest removal timeline. that's very subjective. Stage 2 already lets people know our general rules. I think this would just be misleading most of the time.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. It's pretty easy to do the date math on the day we declare something deprecated. Having that date in the deprecation notice (and also as a substitute for your suggestion at line 74 above) will remind us to actually remove it when we can.

- The change is documented in the CHANGELOG under a `### Deprecated` heading.
- An announcement is posted in the project's Discord and, where applicable, linked from the relevant GitHub issue or PR.

### Stage 2: Grace period

The grace period is a timeframe during which the deprecated API remains operational before being retired. Our minimums are:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The grace period is a timeframe during which the deprecated API remains operational before being retired. Our minimums are:
During the grace period, the deprecated API remains operational before being retired. The _minimum_ grace periods are shown here.


| SDK maturity | Minimum grace period |
| -- | -- |
| Pre-1.0 | 60 days |
| Post-1.0 | 90 days |

During the grace period, the deprecated API will continue to work without functional regression. **Exception:** We may remove deprecated APIs before this window expires if needed to address serious security issues or vulnerabilities.

### Stage 3: Removal
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Stage 3: Removal
### Stage 3: Removal
In the final stage, the item is actually removed from the API:


- The deprecated item is removed in the next major release after the announced grace period has elapsed. **Exception:** A minor release may be used for these cases:
- The item was marked as deprecated prior to the 1.0.0 release.
- The item was only ever made public via a non-default feature/build configuration.
- The migration guide (see "Migration guides", below) is updated to reflect the removal as permanent.

## Language-specific deprecation annotations

Deprecation warnings will be expressed using each language's idiomatic mechanism so that developers are alerted by their compiler or toolchain.

### Rust

```rust
#[deprecated(since = "0.5.0", note = "Use `Builder::new_v2()` instead. Will be removed in 0.7.0.")]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way we are going to know what version will remove the code. Versions are not fixed to a timeline. It's hard enough to estimate the version we deprecate given the way our builds go.

pub fn old_builder() -> Builder { ... }
```

### Python

Use `warnings.warn()` with `DeprecationWarning`:

```python
import warnings

def old_function():
warnings.warn(
"old_function() is deprecated since 0.5.0; use new_function() instead." " Will be removed in 0.7.0.", DeprecationWarning, stacklevel=2,
)
```

The `@warnings.deprecated()` decorator can be used on a class, function, or method to mark it as deprecated. By default, it raises a runtime `DeprecationWarning` and also enables static type checkers to surface the deprecation at the call site. [Python](https://peps.python.org/pep-0702/)

### JavaScript / Node.js

Use the `/** @deprecated */` JSDoc tag for IDE/toolchain visibility, and optionally emit a `console.warn` or Node.js `process.emitWarning` at runtime for dynamic detection.

### C / C++

```C++
__attribute__((deprecated("message")))``[[deprecated("message")]]
```

Also use `__declspec(deprecated)` to ensure deprecations are visible on Windows platforms.

### Swift

Use `@available(*, deprecated, renamed: "newFunction", message: "Use newFunction() instead.")`.

### Kotlin

Use `@Deprecated(message = "...", replaceWith = ReplaceWith("newFunction()"))`.

## Migration guides

Every deprecation will be accompanied by a migration guide. We will provide alternatives or newer versions for deprecated features — if an item is scheduled for removal, users should know the recommended replacement.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the principal of letting people know that something is being removed and why. We should also let them know about alternatives. But the idea that every deprecation requires a before/after code comparison seems too much. Some should have this treatment, but not all. We have a LOT to deprecate. For some things no one ever knew about them and there were no previous code examples.


Migration guides should be published as:

- A section in the CHANGELOG entry for the deprecating release
- A page or section in the SDK documentation site (linking from the deprecated symbol's doc comment)
- A note in any relevant GitHub issue or discussion thread

Guides will include: the reason for the change, a before/after code comparison, any behavioral differences to be aware of, and the removal timeline.

## Communication channels

Send announcements about deprecations through channels where the developer community is active — mailing lists, forums, and platforms like GitHub. Our standard channels are:

- **CHANGELOG.md:** required for every deprecation
- **GitHub Release Notes:** summary of deprecations in each release. These are also reproduced in the doc site.
- **Doc site:** deprecated symbols are visually flagged in all API references where the deprecated APIs are documented.

## Security and bug-fix exceptions

If a deprecated API contains a security vulnerability, we reserve the right to either patch it in place or accelerate its removal, with as much notice as is practical given the severity. In such cases, we will coordinate with known downstream users and post a security advisory.

## Feedback and exceptions

If the deprecation timeline creates a significant hardship for your project, please open a GitHub issue. We will consider extension requests, particularly for users with demonstrated adoption. Our goal is to evolve the SDK without leaving the community behind.