Skip to content

Commit 264d98d

Browse files
authored
Merge pull request #388 from microsoft/mdw-deprecation-considerations-update
Updates to Considerations document
2 parents f610b0e + b618b70 commit 264d98d

File tree

2 files changed

+40
-11
lines changed

2 files changed

+40
-11
lines changed

azure/ConsiderationsForServiceDesign.md

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ Understanding how your service is used and defining its model and interaction pa
7777

7878
:white_check_mark: **DO** create an [OpenAPI Definition](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md) (with [autorest extensions](https://github.com/Azure/autorest/blob/master/docs/extensions/readme.md)) describing the service. The OpenAPI definition is a key element of the Azure SDK plan and is essential for documentation, usability and discoverability of services.
7979

80+
## Design for Change Resiliency
81+
As you build out your service and API, there are a number of decisions that can be made up front that add resiliency to client implementations. Addressing these as early as possible will help you iterate faster and avoid breaking changes.
82+
83+
:ballot_box_with_check: **YOU SHOULD** use extensible enumerations. Extensible enumerations are modeled as strings - expanding an extensible enumeration is not a breaking change.
84+
85+
:ballot_box_with_check: **YOU SHOULD** implement [conditional requests](https://tools.ietf.org/html/rfc7232) early. This allows you to support concurrency, which tends to be a concern later on.
86+
8087
## Use Previews to Iterate
8188
Before releasing your API plan to invest significant design effort, get customer feedback, & iterate through multiple preview releases. This is especially important for V1 as it establishes the abstractions and patterns that developers will use to interact with your service.
8289

@@ -90,6 +97,13 @@ Before releasing your API plan to invest significant design effort, get customer
9097

9198
:ballot_box_with_check: **YOU SHOULD** capture what you have learned during the preview stage and share these findings with your team and with the API Stewardship Board.
9299

100+
## Communicate Deprecations
101+
As your service evolves over time, it will be natural that you want to remove operations that are no longer needed. For example, additional requirements or new capability in your service, may have resulted in a new operation that, effectively, replaces an old one.
102+
Azure has a well established breaking changes policy that describes how to approach these kinds of changes. As part of this policy, the service team is required to clearly communicate to customers when their API is changing, e.g. deprecating operations. Often, this is done via an email to the address that is attached to the Azure subscription.
103+
104+
However, given how many organizations are structured, it's common that this email address is different from the actual people writing code against your API. To address this, the service API should declare that it may return the `azure-deprecating` header, to indicate that this operation will be removed in the future. There is a simple string convention, specied in the [Azure REST API Guidelines](https://aka.ms/azapi/guidelines) that provides more information about the forthcoming deprecation.
105+
This header is targeted at developers or operation professionals, and it is intended to give them enough information and lead time to properly adapt to this change. Your documentation should reference this header and encourage logging and alerting practices based on its presence.
106+
93107
## Avoid Surprises
94108
A major inhibitor to adoption and usage is when an API behaves in an unexpected way. Often, these are subtle design decisions that seem benign at the time, but end up introducing significant downstream friction for developers.
95109

@@ -116,13 +130,6 @@ Another important design pattern for avoiding surprises is idempotency. An opera
116130
HTTP requires certain operations like GET, PUT, and DELETE to be idempotent, but for cloud services it is important to make _all_ operations idempotent so that clients can use retry in failure scenarios without risk of unintended consequences.
117131
See the [HTTP Request / Response Pattern section of the Guidelines](./Guidelines.md#http-request--response-pattern) for detailed guidance on making operations idempotent.
118132

119-
## Design for Change Resiliency
120-
As you build out your service and API, there are a number of decisions that can be made up front that add resiliency to client implementations. Addressing these as early as possible will help you iterate faster and avoid breaking changes.
121-
122-
:ballot_box_with_check: **YOU SHOULD** use extensible enumerations. Extensible enumerations are modeled as strings - expanding an extensible enumeration is not a breaking change.
123-
124-
:ballot_box_with_check: **YOU SHOULD** implement [conditional requests](https://tools.ietf.org/html/rfc7232) early. This allows you to support concurrency, which tends to be a concern later on.
125-
126133
## Action Operations
127134

128135
Most operations conform to one of the standard REST Create, Read, Update, Delete, or List (CRUDL) style of operations. We refer to all other operations as "action" operations. Some examples of action operations are to reboot a VM, or send an email.
@@ -362,17 +369,23 @@ You should use appropriate [HTTP status codes](https://developer.mozilla.org/doc
362369
- A `404` status code tells them the blob doesn't exist and the customer can report the error to their users
363370
- A `BlobNotFound` or `ContainerNotFound` error code will tell them why the blob doesn't exist so they can take steps to recreate it
364371

365-
The [common error schema in the Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#handling-errors) allows nested details and inner errors that have their own error codes, but the top-level error code is the most important. The HTTP status code and the top-level error code are the only part of your error that we consider part of your API contract that follows the same compatibility requirements as the rest of your API. Importantly, this means you **changing the HTTP status code or top-level error code for an API is a breaking change**. You can only return new status codes and error codes in future API versions if customers make use of new features that trigger new classes of errors. Battle tested error handling is some of the hardest code to get right and we can't break that for customers when they upgrade to the latest version. The rest of the properties in your error like `message`, `details`, etc., are not considered part of your API contract and can change to improve the diagnosability of your service.
372+
The [common error schema in the Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#handling-errors) allows nested details and inner errors that have their own error codes, but the top-level error code is the most important. The HTTP status code and the top-level error code are the only part of your error that we consider part of your API contract that follows the same compatibility requirements as the rest of your API.
373+
Importantly, this means you **changing the HTTP status code or top-level error code for an API is a breaking change**.
374+
You can only return new status codes and error codes in future API versions if customers make use of new features that trigger new classes of errors. Battle tested error handling is some of the hardest code to get right and we can't break that for customers when they upgrade to the latest version. The rest of the properties in your error like `message`, `details`, etc., are not considered part of your API contract and can change to improve the diagnosability of your service.
366375

367-
You should also return the top-level error code as the `x-ms-error-code` response header so client libraries have the ability to automatically retry requests when possible without having to parse a JSON payload. We recommend unique error codes like `ContainerBeingDeleted` for every distinct recoverable error that can occur, but suggest reusing common error codes like `InvalidHeaderValue` for usage errors where a descriptive error message is more important for resolving the problem. The Storage [Common](https://docs.microsoft.com/rest/api/storageservices/common-rest-api-error-codes) and [Blob](https://docs.microsoft.com/rest/api/storageservices/blob-service-error-codes) error codes are a good starting point if you're looking for examples. You can [define an enum in your spec](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/storage/data-plane/Microsoft.BlobStorage/preview/2021-04-10/blob.json#L10419) with `"modelAsString": true` that lists all of the top-level error codes to make it [easier for your customers to handle specific error codes](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/storage/Azure.Storage.Blobs#troubleshooting).
376+
You should also return the top-level error code as the `x-ms-error-code` response header so client libraries have the ability to automatically retry requests when possible without having to parse a JSON payload. We recommend unique error codes like `ContainerBeingDeleted` for every distinct recoverable error that can occur, but suggest reusing common error codes like `InvalidHeaderValue` for usage errors where a descriptive error message is more important for resolving the problem.
377+
The Storage [Common](https://docs.microsoft.com/rest/api/storageservices/common-rest-api-error-codes) and [Blob](https://docs.microsoft.com/rest/api/storageservices/blob-service-error-codes) error codes are a good starting point if you're looking for examples.
378+
You can [define an enum in your spec](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/storage/data-plane/Microsoft.BlobStorage/preview/2021-04-10/blob.json#L10419) with `"modelAsString": true` that lists all of the top-level error codes to make it [easier for your customers to handle specific error codes](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/storage/Azure.Storage.Blobs#troubleshooting).
368379

369380
You should not document specific error status codes in your OpenAPI/Swagger spec. The `"default"` response is the only thing AutoRest considers an error response unless you provide other annotations. Every unique status code turns into a separate code path in your client libraries so we do not encourage this practice. The only reason to document specific error status codes is if they return a different error response than the default, but that is also heavily discouraged.
370381

371-
Be as precise as possible when writing error messages. A message with just `Invalid Argument` is almost useless to a customer who sent 100KB of JSON to your endpoint. ``Query parameter `top` must be less than or equal to 1000`` tells a customer exactly what went wrong so they can quickly fix the problem. Don't go overboard while writing great, understandable error messages and include any sensitive customer information or secrets though. Many developers will blindly write any error to logs that don't have the same level of access control as Azure resources.
382+
Be as precise as possible when writing error messages. A message with just `Invalid Argument` is almost useless to a customer who sent 100KB of JSON to your endpoint. ``Query parameter `top` must be less than or equal to 1000`` tells a customer exactly what went wrong so they can quickly fix the problem.
383+
Don't go overboard while writing great, understandable error messages and include any sensitive customer information or secrets though. Many developers will blindly write any error to logs that don't have the same level of access control as Azure resources.
372384

373385
All responses should include the `x-ms-request-id` header with a unique id for the request, but this is particularly important for error responses. Service logs for the request should contain the `x-ms-request-id` so that support staff can use this value to diagnose specific customer reported errors.
374386

375-
Finally, write sample code for your service's workflow and add the code you'd want customers using to gracefully recover from errors. Is it actually graceful? Is it something you'd be comfortable asking most customers to write? We also highly encourage reaching out to customers during private preview and asking them for code they've written against your service. Their error handling might match your expectations, you might find a strong need for better documentation, or you might find important opportunities to improve the errors you're returning.
387+
Finally, write sample code for your service's workflow and add the code you'd want customers using to gracefully recover from errors. Is it actually graceful? Is it something you'd be comfortable asking most customers to write?
388+
We also highly encourage reaching out to customers during private preview and asking them for code they've written against your service. Their error handling might match your expectations, you might find a strong need for better documentation, or you might find important opportunities to improve the errors you're returning.
376389

377390
## Pagination
378391

azure/Guidelines.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
| Date | Notes |
88
| ----------- | -------------------------------------------------------------- |
9+
| 2022-Aug-20 | Add "azure-deprecating" response header |
910
| 2022-Jul-15 | Update guidance on long-running operations |
1011
| 2022-May-11 | Drop guidance on version discovery |
1112
| 2022-Mar-29 | Add guidelines about using durations |
@@ -191,6 +192,7 @@ _x-ms-request-id_ | Response | 4227cdc5-9f48-4e84-921a-10967cb785a0
191192
ETag | Response | "67ab43" (see [Conditional Requests](#Conditional-Requests))
192193
last-modified | Response | Sun, 06 Nov 1994 08:49:37 GMT
193194
_x-ms-error-code_ | Response | (see [Handling Errors](#Handling-Errors))
195+
_azure-deprecating_ | Response | (see [Deprecating Behavior](#Deprecating-Behavior))
194196
retry-after | Response | 180 (see [RFC 7231, Section 7.1.3](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3))
195197

196198
:white_check_mark: **DO** support all headers shown in _italics_
@@ -749,6 +751,20 @@ While removing a value from an enum is a breaking change, adding value to an enu
749751

750752
> :ballot_box_with_check: **You SHOULD** use extensible enums unless you are positive that the symbol set will **NEVER** change over time.
751753
754+
### Deprecating Behavior Notification
755+
756+
When the [API Versioning](#API-Versioning) guidance above cannot be followed and the [Azure Breaking Change Reviewers](mailto:[email protected]) approve a breaking change to the operation/service, the operation/service that is being deprecated must add the `azure-deprecating` response header with a semicolon-delimited string notifying the caller what is being deprecated, when it will no longer function, and a URL linking to more information. The purpose is to inform customers (when debugging/logging responses) that they must take action to modify their call to the service's operation or their call will soon stop working entirely. It is not expected that client code will examine/parse this header's value in any way; it is purely informational to a human being.
757+
758+
:white_check_mark: **DO** add the 'azure-deprercating' header with a string value to all service operations in your service's contract file (cadl/swagger).
759+
760+
:white_check_mark: **DO** include this header in the operation's response _only if_ the operation will stop working in the future and the client _must take_ action in order for it to keep working. NOTE: We do not want to scare customers with this header.
761+
762+
:white_check_mark: **DO** make the header's value a semicolon-delimited string indicating a set of deprecations where each one indicates what is deprecating, when it is deprecating, and a URL to more information. For example:
763+
764+
```text
765+
azure-deprecating: api-version=2009-27-07 will stop working on 2022-12-01 (https://azure.microsoft.com/en-us/updates/video-analyzer-retirement);TLS 1.0 & 1.1 will stop working on 2020-10-30 (https://azure.microsoft.com/en-us/updates/azure-active-directory-registration-service-is-ending-support-for-tls-10-and-11/)
766+
```
767+
752768
### Repeatability of requests
753769

754770
The ability to retry failed requests for which a client never received a response greatly simplifies the ability to write resilient distributed applications. While HTTP designates some methods as safe and/or idempotent (and thus retryable), being able to retry other operations such as create-using-POST-to-collection is desirable.

0 commit comments

Comments
 (0)