# Backwards Compatibility

AWS SDKs require that clients can evolve in a backwards compatible way as new fields and operations are added. The types
generated by `smithy-rs` are specifically designed to meet these requirements. Specifically, the following
transformations must not break compilation when upgrading to a new version:

- [New operation added](#new-operation-added)
- [New member added to structure](#new-member-added-to-structure)
- [New union variant added](#new-union-variant-added)
- New error added (todo)
- New enum variant added (todo)

However, the following changes are _not_ backwards compatible:

- Error **removed** from operation.

In general, the best tool in Rust to solve these issues in the `#[non_exhaustive]` attribute which will be explored in
detail below.

## New Operation Added

**Before**

```smithy
$version: "1"
namespace s3

service S3 {
    operations: [GetObject]
}
```

**After**

```smithy
$version: "1"
namespace s3

service S3 {
    operations: [GetObject, PutObject]
}
```

Adding support for a new operation is backwards compatible because SDKs to not expose any sort of "service trait" that
provides an interface over an entire service. This _prevents_ clients from inheriting or implementing an interface that
would be broken by the addition of a new operation.

## New member added to structure

### Summary

- Structures are marked `#[non_exhaustive]`
- Structures must be instantiated using builders
- Structures must not derive `Default` in the event that required fields are added in the future.

In general, adding a new `public` member to a structure in Rust is not backwards compatible. However, by applying
the `#[non_exhaustive]` to the structures generated by the Rust SDK, the Rust compiler will prevent users from using our
structs in ways that prevent new fields from being added in the future. **Note**: in this context, the optionality of
the fields is irrelevant.

Specifically, [`#[non_exhaustive]`](https://doc.rust-lang.org/reference/attributes/type_system.html) prohibits the
following patterns:

1. Direct structure instantiation:
   ```rust
   # fn foo() {
   let ip_addr = IpAddress { addr: "192.168.1.1" };
   # }
   ```
   If a new member `is_local: boolean` was added to the IpAddress structure, this code would not compile. To enable
   users to still construct
   our structures while maintaining backwards compatibility, all structures expose a builder, accessible
   at `SomeStruct::Builder`:

   ```rust
   # fn foo() {
   let ip_addr = IpAddress::builder().addr("192.168.1.1").build();
   # }
   ```
2. Structure destructuring:
   ```rust
   # fn foo() {
   let IpAddress { addr } = some_ip_addr();
   # }
   ```
   This will also fail to compile if a new member is added, however, by adding `#[non_exhaustive]`, the `..` multifield
   wildcard MUST be added to support new fields being added in the future:
   ```rust
   # fn foo() {
   let IpAddress { addr, .. } = some_ip_addr();
   # }
   ```

### Validation & Required Members

**Adding a required member to a structure is _not_ considered backwards compatible.** When a required member is added to
a structure:

1. The builder will change to become fallible, meaning that instead of returning `T` it will
   return `Result<T, BuildError>`.
2. Previous builder invocations that did not set the new field will still stop compiling if this was the first required
   field.
3. Previous builder invocations will now return a `BuildError` because the required field is unset.

## New union variant added

Similar to structures, `#[non_exhaustive]` also applies to unions. In order to allow new union variants to be added in
the future, all unions (`enum` in Rust) generated by the Rust SDK must be marked with `#[non_exhaustive]`. **Note**:
because new fields cannot be added to union variants, the union variants themselves do **not** need
to be `#[non_exhaustive]`. To support new variants from services, each union contains an `Unknown` variant. By
marking `Unknown` as non_exhaustive, we prevent customers from instantiating it directly.

```rust
#[non_exhaustive]
#[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)]
pub enum AttributeValue {
    B(aws_smithy_types::Blob),
    Bool(bool),
    Bs(std::vec::Vec<aws_smithy_types::Blob>),
    L(std::vec::Vec<crate::model::AttributeValue>),
    M(std::collections::HashMap<std::string::String, crate::model::AttributeValue>),
    N(std::string::String),
    Ns(std::vec::Vec<std::string::String>),
    Null(bool),
    S(std::string::String),
    Ss(std::vec::Vec<std::string::String>),

    // By marking `Unknown` as non_exhaustive, we prevent client code from instantiating it directly.
    #[non_exhaustive]
    Unknown,
}
```
