diff --git a/aep/general/0180/aep.md.j2 b/aep/general/0180/aep.md.j2 index c9189ebe..3381a26c 100644 --- a/aep/general/0180/aep.md.j2 +++ b/aep/general/0180/aep.md.j2 @@ -1,5 +1,246 @@ # Backwards compatibility -**Note:** This AEP has not yet been adopted. See -[this GitHub issue](https://github.com/aep-dev/aep.dev/issues/8) for more -information. +APIs are fundamentally contracts with users, and users often write code against +APIs that is then launched into a production service with the expectation that +it continues to work (unless the API has a [stability level][aep-181] that +indicates otherwise). Therefore, it is important to understand what constitutes +a backwards compatible change and what constitutes a backwards incompatible +change. + +## Guidance + +Existing client code **must not** be broken by a service updating to a new +minor or patch release. Old clients **must** be able to work against newer +servers (with the same major version number). + +**Important:** It is not always clear whether a change is compatible or not. +The guidance here **should** be treated as indicative, rather than as a +comprehensive list of every possible change. + +There are three distinct types of compatibility to consider: + +1. Source compatibility: Code written against a previous version **must** + compile against a newer version, and successfully run with a newer version + of the client library. +2. Wire compatibility: Code written against a previous version **must** be able + to communicate correctly with a newer server. In other words, not only are + inputs and outputs compatible, but the serialization and deserialization + expectations continue to match. +3. Semantic compatibility: Code written against a previous version **must** + continue to receive what most reasonable developers would expect. (This can + be tricky in practice, however, and sometimes determining what users will + expect can involve a judgment call.) + +**Note:** In general, the specific guidance here assumes use of protocol +buffers and JSON as transport formats. Other transport formats may have +slightly different rules. + +**Note:** This guidance assumes that APIs are intended to be called from a +range of consumers, written in multiple languages and with no control over +how and when consumers update. Any API which has a more limited scope (for +example, an API which is only called by client code written by the same team +as the API producer, or deployed in a way which can enforce updates) should +carefully consider its own compatibility requirements. + +### Adding components + +In general, new components (interfaces, methods, messages, fields, enums, or +enum values) **may** be added to existing APIs in the same major version. + +However, keep the following guidelines in mind when doing this: + +- Code written against the previous surface (and thus is unaware of the new + components) **must** continue to be treated the same way as before. + - New required fields **must not** be added to existing request messages or + resources. + - Any field being populated by clients **must** have a default behavior + matching the behavior before the field was introduced. + - Any field previously populated by the server **must** continue to be + populated, even if it introduces redundancy. +- For enum values specifically, be aware that it is possible that user code + does not handle new values gracefully. + - Enum values **may** be freely added to enums which are only used in request + messages. + - Enums that are used in response messages or resources and which are + expected to receive new values **should** document this. Enum values still + **may** be added in this situation; however, appropriate caution **should** + be used. + +**Note:** It is possible when adding a component closely related to an existing +component (for example, `string foo_value` when `string foo` already exists) to +enter a situation where generated code will conflict. Service owners **should** +be aware of subtleties in the tooling they or their users are likely to use +(and tool authors **should** endeavor to avoid such subtleties if possible). + +### Removing or renaming components + +Existing components (interfaces, methods, messages, fields, enums, or enum +values) **must not** be removed from existing APIs in the same major version. +Removing a component is a backwards incompatible change. + +**Important:** Renaming a component is semantically equivalent to "remove and +add". In cases where these sorts of changes are desirable, a service **may** +add the new component, but **must not** remove the existing one. In situations +where this can allow users to specify conflicting values for the same semantic +idea, the behavior **must** be clearly specified. + +### Moving components between files + +Existing components **must not** be moved between files. + +Moving a component from one proto file to another within the same package is +wire compatible, however, the code generated for languages like C++ or Python +will result in breaking change since `import` and `#include` will no longer +point to the correct code location. + +### Moving into oneofs + +Existing fields **must not** be moved into or out of a oneof. This is a +backwards-incompatible change in the Go protobuf stubs. + +### Changing the type of fields + +Existing fields and messages **must not** have their type changed, even if the +new type is wire-compatible, because type changes alter generated code in a +breaking way. + +### Changing resource paths + +A resource **must not** change its [path][aep-122]. + +Unlike most breaking changes, this affects major versions as well: in order for +a client to expect to use v2.0 access a resource that was created in v1.0 or +vice versa, the same resource name **must** be used in both versions. + +More subtly, the set of valid resource paths **should not** change either, for +the following reasons: + +- If resource name formats become more restrictive, a request that would + previously have succeeded will now fail. +- If resource name formats become less restrictive than previously documented, + then code making assumptions based on the previous documentation could break. + Users are very likely to store resource names elsewhere, in ways that may be + sensitive to the set of permitted characters and the length of the name. + Alternatively, users might perform their own resource name validation to + follow the documentation. + - For example, Amazon gave customers [a lot of warning][ec2] and had a + migration period when they started allowing longer EC2 resource IDs. + +### Semantic changes + +Code will often depend on API behavior and semantics, _even when such behavior +is not explicitly supported or documented_. Therefore, APIs **must not** change +visible behavior or semantics in ways that are likely to break reasonable user +code, as such changes will be seen as breaking by those users. + +**Note:** This does involve some level of judgment; it is not always clear +whether a proposed change is likely to break users, and an expansive reading of +this guidance could ostensibly prevent _any_ change (which is not the intent). + +#### Default values must not change + +Default values are the values set by servers for resources when they are not +specified by the client. This section only applies to static default values within +fields on resources and does not apply to dynamic defaults such as the default IP +address of a resource. + +Changing the default value is considered breaking and **must not** be done. The +default behavior for a resource is determined by its default values, and this +**must not** change across minor versions. + +For example: + +```proto +message Book { + // google.api.resource and other annotations and fields + + // The genre of the book + // If this is not set when the book is created, the field will be given a value of FICTION. + enum Genre { + UNSPECIFIED = 0; + FICTION = 1; + NONFICTION = 2; + } +} +``` + +Changing to: + +```proto +message Book { + // google.api.resource and other annotations and fields + + // The genre of the book + // If this is not set when the book is created, the field will be given a value of NONFICTION. + enum Genre { + UNSPECIFIED = 0; + FICTION = 1; + NONFICTION = 2; + } +} +``` + +would constitute a breaking change. + +#### Serializing defaults + +APIs **must not** change the way a field with a default value is serialized. For +example if a field does not appear in the response if the value is equal to the +default, the serialization **must not** change to include the field with the +default. Clients may depend on the presence or absence of a field in a resource +as semantically meaningful, so a change to how serialization is done for absent +values **must not** occur in a minor version. + +Consider the following proto, where the default value of `wheels` is `2`: + +```proto +// A representation of an automobile +message Automobile { + // google.api.resource and other annotations and fields + + // The number of wheels on the automobile. + // The default value is 2, when no value is sent by the client. + int wheels = 2; +} +``` + +First the proto serializes to JSON when the value of `wheels` is `2` as follows: + +```json +{ + "name": "my-car" +} +``` + +Then, the API service changes the serialization to include `wheel` even if the +value is equal to the default value, `2` as follows: + +```json +{ + "name": "my-car", + "wheels": 2 +} +``` + +This constitutes a change that is not backwards compatible within a major +version. + +## Further reading + +- For compatibility around field behavior, see [AEP-203][]. +- For compatibility around pagination, see [AEP-158][]. +- For compatibility around long-running operations, see [AEP-151][]. +- For understanding stability levels and expectations, see [AEP-181][]. +- For compatibility with client library resource name parsing, see [AEP-4231][] +- For compatibility with client library method signatures, see [AEP-4232][] + + +[aep-122]: ./0122.md +[aep-151]: ./0151.md +[aep-158]: ./0158.md +[aep-181]: ./0181.md +[aep-203]: ./0203.md +[aep-4231]: ../client-libraries/4231.md +[aep-4232]: ../client-libraries/4232.md +[ec2]: https://aws.amazon.com/blogs/aws/theyre-here-longer-ec2-resource-ids-now-available/ + diff --git a/aep/general/0180/aep.yaml b/aep/general/0180/aep.yaml index ec739346..cfb24135 100644 --- a/aep/general/0180/aep.yaml +++ b/aep/general/0180/aep.yaml @@ -1,7 +1,7 @@ --- id: 180 -state: reviewing +state: approved slug: backwards-compatibility -created: 2023-01-22 +created: 2024-05-31 placement: category: best-practices