RFC: Behavior Versions

Status: RFC

Applies to: client

For a summarized list of proposed changes, see the Changes Checklist section.

This RFC describes "Behavior Versions," a mechanism to allow SDKs to ship breaking behavioral changes like a new retry strategy, while allowing customers who rely on extremely consistent behavior to evolve at their own pace.

By adding behavior major versions (BMV) to the Rust SDK, we will make it possible to ship new secure/recommended defaults to new customers without impacting legacy customers.

The fundamental issue stems around our inability to communicate and decouple releases of service updates and behavior within a single major version.

Both legacy and new SDKs have the need to alter their SDKs default. Historically, this caused new customers on legacy SDKs to be subject to legacy defaults, even when a better alternative existed.

For new SDKs, a GA cutline presents difficult choices around timeline and features that can’t be added later without altering behavior.

Both of these use cases are addressed by Behavior Versions.

The user experience if this RFC is implemented

In the current version of the SDK, users can construct clients without indicating any sort of behavior major version. Once this RFC is implemented, there will be two ways to set a behavior major version:

  1. In code via aws_config::defaults(BehaviorVersion::latest()) and <service>::Config::builder().behavior_version(...). This will also work for config_override.
  2. By enabling behavior-version-latest in either aws-config (which brings back from_env) OR a specific generated SDK crate
# Cargo.toml
[dependencies]
aws-config = { version = "1", features = ["behavior-version-latest"] }
# OR
aws-sdk-s3 = { version = "1", features = ["behavior-version-latest"] }

If no BehaviorVersion is set, the client will panic during construction.

BehaviorVersion is an opaque struct with initializers like ::latest(), ::v2023_11_09(). Downstream code can check the version by calling methods like ::supports_v1()

When new BMV are added, the previous version constructor will be marked as deprecated. This serves as a mechanism to alert customers that a new BMV exists to allow them to upgrade.

How to actually implement this RFC

In order to implement this feature, we need to create a BehaviorVersion struct, add config options to SdkConfig and aws-config, and wire it throughout the stack.

#![allow(unused)]
fn main() {
/// Behavior major-version of the client
///
/// Over time, new best-practice behaviors are introduced. However, these behaviors might not be backwards
/// compatible. For example, a change which introduces new default timeouts or a new retry-mode for
/// all operations might be the ideal behavior but could break existing applications.
#[derive(Debug, Clone)]
pub struct BehaviorVersion {
    // currently there is only 1 MV so we don't actually need anything in here.
    _private: (),
}
}

To help customers migrate, we are including from_env hooks that set behavior-version-latest that are deprecated. This allows customers to see that they are missing the required cargo feature and add it to remove the deprecation warning.

Internally, BehaviorVersion will become an additional field on <client>::Config. It is not ever stored in the ConfigBag or in RuntimePlugins.

When constructing the set of "default runtime plugins," the default runtime plugin parameters will be passed the BehaviorVersion. This will select the correct runtime plugin. Logging will clearly indicate which plugin was selected.

Design Alternatives Considered

An original design was also considered that made BMV optional and relied on documentation to steer customers in the right direction. This was deemed too weak of a mechanism to ensure that customers aren't broken by unexpected changes.

Changes checklist

  • Create BehaviorVersion and the BMV runtime plugin
  • Add BMV as a required runtime component
  • Wire up setters throughout the stack
  • Add tests of BMV (set via aws-config, cargo features & code params)
  • Remove aws_config::from_env deprecation stand-ins We decided to persist these deprecations
  • Update generated usage examples