The problem
The Rust SDK currently has a forwards-compatibility problem, specifically when it comes to our protocol types.
Changes, which in many other SDKs would only require minor version bumps, often end up being public-API breaking changes in the Rust SDK due to the particularities of the Rust language and how this SDK is architected. Specifically, because many of our telemetry types (e.g. Event, Log, Metric) are defined as public structs with all-public fields in sentry-types, adding a field to any of these types is a breaking change. Likewise, the client options are also all public, so adding an additional configuration option is also breaking.
As additions need to happen sometimes, this presents a real challenge, especially if we ever want to release a 1.x version of the Rust SDK. Even without a 1.x release, it presents a potential obstacle for our users' ability to stay up to date with the latest SDK versions.
Constraints
We still want to provide users with fully-public type definitions for all of our protocol types. This allows them to perform arbitrary filtering and data modification within before_send callbacks.
What we can do about it
The main improvement will be to make it possible to add fields to telemetry types without requiring breaking changes, while still enabling these types to be fully public, with all public fields, so they can be modified.
To do this, we can mark all these protocol types as #[non_exhaustive]. Doing so will require us to add minimal constructors and/or Default implementations to allow for construction outside of sentry-types.
For the protocol types specifically, we also may wish to add more minimal builder types that are exposed in sentry-core. These would only allow setting the fields that users should be responsible for setting; everything we attach automatically, and which should not usually be set by users (e.g. trace_id) would be set internally by the SDK when we construct the protocol type. There is a WIP example of this for metrics on this branch. We would then only allow users to touch the full protocol types in before_send callbacks.
Other future-compatibility improvements
Some other things we should consider doing:
- Avoid public re-exports from other crates, where not necessary, so breaking changes are scoped to as few crates as possible. In particular, we should never use
* in any public re-exports (and should enable a lint to enforce this, if one exists).
- More carefully consider when adding something, whether it should be public. If an API does not need to be public, it probably should stay private or
pub(crate).
- Following from the above, we should add a CI job to programmatically check how PRs change the public API. The job should detect and highlight (e.g. via a PR comment) when a PR changes the API, and ideally, it will also detect whether the changes are breaking or non-breaking.
The problem
The Rust SDK currently has a forwards-compatibility problem, specifically when it comes to our protocol types.
Changes, which in many other SDKs would only require minor version bumps, often end up being public-API breaking changes in the Rust SDK due to the particularities of the Rust language and how this SDK is architected. Specifically, because many of our telemetry types (e.g.
Event,Log,Metric) are defined as public structs with all-public fields insentry-types, adding a field to any of these types is a breaking change. Likewise, the client options are also all public, so adding an additional configuration option is also breaking.As additions need to happen sometimes, this presents a real challenge, especially if we ever want to release a
1.xversion of the Rust SDK. Even without a1.xrelease, it presents a potential obstacle for our users' ability to stay up to date with the latest SDK versions.Constraints
We still want to provide users with fully-public type definitions for all of our protocol types. This allows them to perform arbitrary filtering and data modification within
before_sendcallbacks.What we can do about it
The main improvement will be to make it possible to add fields to telemetry types without requiring breaking changes, while still enabling these types to be fully public, with all public fields, so they can be modified.
To do this, we can mark all these protocol types as
#[non_exhaustive]. Doing so will require us to add minimal constructors and/orDefaultimplementations to allow for construction outside ofsentry-types.For the protocol types specifically, we also may wish to add more minimal builder types that are exposed in
sentry-core. These would only allow setting the fields that users should be responsible for setting; everything we attach automatically, and which should not usually be set by users (e.g.trace_id) would be set internally by the SDK when we construct the protocol type. There is a WIP example of this for metrics on this branch. We would then only allow users to touch the full protocol types inbefore_sendcallbacks.Other future-compatibility improvements
Some other things we should consider doing:
*in any public re-exports (and should enable a lint to enforce this, if one exists).pub(crate).