Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced Operation Deprecation and versioning #782

Open
darrelmiller opened this issue Sep 9, 2016 · 18 comments
Open

Enhanced Operation Deprecation and versioning #782

darrelmiller opened this issue Sep 9, 2016 · 18 comments
Labels
versioning describing versions of APIs/endpoints/operations

Comments

@darrelmiller
Copy link
Member

Currently, OpenAPI allows us to identify the version of the API being described and deprecate operations using a Boolean value.

I would like to propose some additional metadata that makes it easier to track changes to APIs.
In addition to the existing deprecated flag, there is now a deprecatedVersion and replacementOperationId property.

Consider the following API and two of its operations:

{
 "openapi" : "3.0.0",
 "info" :  {
      "title" : "My API",
      "version" : "1.0.313"
  }
{
    "/foo" : {
        "get" : {
             "operationId" : "getFoo",
             "description" : "Return a foo",
             "deprecated" : true,
             "deprecatedVersion" : "1.0.312",
             "replacementOperationId" : "getFoo2"
             "responses" : {
                 "200" : {
                        "description" : "A foo representation"
                 }
             } 

        },
   "/foo2" : {
        "get" : {
             "operationId" : "getFoo2",
             "description" : "Return a foo",
             "responses" : {
                 "200" : {
                     "description" : "A completely different breaking foo representation"   
                 }
             } 
        }
    }
}
}

The deprecatedVersion property indicates the API version in which this operation was deprecated. The replacementOperationId provides a pointer to a new operation that replaces the functionality of the deprecated operation.

These new properties enable a variety of useful capabilities:

  • Display documentation that is relevant to a particular API version. Operations that were deprecated prior to that version can be hidden by default to prevent accidently usage of operations that have been replaced.
  • Help developers migrate to new operations by allowing documentation of deprecated operations to point to replacement operations.
  • Generate client code that only supports operations available in a particular API version.
  • Makes it possible to manage APIs to that are changing incrementally instead of doing big bang updates that completely replace a prior API.
  • Help developers identify what has changed in a new API release.
@ePaul
Copy link
Contributor

ePaul commented Sep 12, 2016

Hmm, maybe also a field indicating until when an API will be supported?

@darrelmiller
Copy link
Member Author

@ePaul It would be nice if we can provide that information. However, how reliable is it going to be? And should it be a date, or should it be a version when it will be removed?

Anyway, I think support for this might have to wait, as we have higher priority breaking changes that we want to get resolved ASAP. We can always experiment with x- properties for stuff like this.

I just wanted to get the idea on record before I forgot it :-)

@timburks
Copy link
Contributor

We would like to also be able to mark an entire API as deprecated. Repeating some internal discussion:

The simplest change to support this would be to add the "deprecated" property at the top level (the "root document object"). If the property is true, the API is deprecated; if not, it's still active.

For API deprecation, some additional information might be useful:

  • The date of the original deprecation.
  • The date of final shutdown.
  • Deprecation strategy:
    • Are new users permitted or forbidden?
    • Is there any SLO for this API?
    • Is there any system degradation due to the deprecation (eg. higher latency, intermittent errors, lower quotas, warning messages in response headers, etc)? This could either be purely informative or this setting could directly affect how the backend behaves.
    • Links to any replacement APIs.

@curry684
Copy link

curry684 commented Feb 1, 2017

We would like to also be able to mark an entire API as deprecated. Repeating some internal discussion:

Came here to upvote this point. The scenario is all too common that you want to phase out v1 of an API end of next year in favor of v2 being released today. We should be able to mark this in the v1 spec document so contract-first clients and API generators can issue a loud warning pointing to the correct and supported API version.

@RobDolinMS RobDolinMS added this to the v3.Next milestone Apr 21, 2017
@srvance
Copy link

srvance commented Jul 25, 2017

  1. In light of the change to the definition of version from Version Enhancement to Compatibility Strategy #834, deprecatedVersion would refer to the version of the OpenAPI document, not the version of the API itself.
  2. How would you deal with APIs that combine or split, rather than just have a one-to-one replacement?

@philsturgeon
Copy link
Contributor

I have been dealing with a similar thing in something I'm working on.

To add some of my thoughts: Lots of APIs don't have a single version, and imposing one would be tough. This should work for an evolving API with a version like "4.0.342" as suggested, but it also needs to work for URL versioned/namespaced APIs (as much as we hate em).

This is why date and version are both important.

Replacements are also interesting, because whilst it would often be ideal to say "this field is gone, use this one", sometimes fields just go without a replacement, or split or change fundamentally.

Pointing callers to the replacement field or the replacement endpoint is one thing, but we gotta send people somewhere when that is not an option... URL to docs? /shrug

@dret
Copy link
Contributor

dret commented Jan 12, 2019

just for completeness: while OpenAPI is about design time, it might be interesting to also consider runtime issues. for deprecating complete APIs, the HTTP Sunset header should be an RFC any minute now (https://tools.ietf.org/html/draft-wilde-sunset-header-10), and that could nicely complement any description information with real-time runtime signals.

@sdatspun2
Copy link

i would refine @dret's comment above. There are two relevant HTTP headers for deprecation: Deprecation (https://tools.ietf.org/html/draft-dalal-deprecation-header-00) and Sunset. To inform about the deprecation, the Deprecation HTTP header should be used. To inform about sunset of the deprecated resource, the Sunset header should be used in addition to the Deprecation header. This is described in section #5 https://tools.ietf.org/html/draft-dalal-deprecation-header-00#section-5.

Draft #11 https://tools.ietf.org/html/draft-wilde-sunset-header-11 of the Sunset header clarifies this aspect as well in section 1.4 https://tools.ietf.org/html/draft-wilde-sunset-header-11#section-1.4.

@dret
Copy link
Contributor

dret commented Mar 25, 2019

yes, completely agree with @sdatspun2 here: HTTP Sunset is only for sunsets, and deprecation should be done with HTTP Deprecation.

@philsturgeon
Copy link
Contributor

Yep, now that both Sunset and Deprecation exist, there is not much need for further work on this. With OpenAPI mostly being used for design time, simply being able to say "This is now deprecated" is enough, as runtime code will sniff for deprecation.

For example, your OpenAPI generated Ruby SDK can just reference faraday-sunset and you will have an error in your logs or bug reporting system if an endpoint is suddenly sunset. Sniffing for deprecations is equally possible. I think all of this is more powerful than trying to get super specific about what might go away when in design time. :)

@kPOWz
Copy link

kPOWz commented Nov 9, 2020

Allowing a Link Object for or instead of replacementOperationId adds the power of expressions to describing the vLatest call

@philsturgeon
Copy link
Contributor

It's a long time since my previous comment and for some reason I now love the idea of "replacementOperationId" : "getFoo2" or similar.

I can already imagine it being used in the new Stoplight docs we're cooking up, it'd be amazing for helping folks power through API evolution.

@philsturgeon
Copy link
Contributor

Design time is often considered something that happens first, then you build, then you run it, but that progression is not linear. In the design-first workflow, design happens first as the name suggests, but also the entire way through the lifecycle, with every new endpoint or property being added to the design over time.

As such it's perfectly normal for runtime code to emit deprecation and sunset headers, those headers could even be powered by a middleware that looks for these deprecation flags in OpenAPI. Seeing as The Stoplight Way is to have your OpenAPI and code in the same repo, this would be incredibly easy to do, and stop the duplication of information between having to add the deprecation flag in the description and then also add it in the code... 🤔

What do folks think?

@yjaaidi
Copy link

yjaaidi commented May 12, 2022

Hi all 👋
What about adding a sunset field?
The sunset field could indicate the next version where the deprecated item will be removed.
It could look like this:

openapi: 3.1.0
info:
  version: 1.0.313
paths:
  /foo:
    get:
      deprecated: true
      sunset: 4.0.0

This allows:

  1. documenting the sunset
  2. implementing a lint rule (with Spectral or such) that triggers an error if we forget to remove something deprecated when the version is bumped. (e.g. the day we bump the spec above to 4.0.0, we would get an error if we don't remove the GET /foo operation
  3. if the API or the API gateway are informed of the upcoming versions' release dates, we can easily automate the presence & value of the Sunset header in the response.

By the way, in order to improve docs and make the 3rd item easier, what about adding the version history or the release planning in the info? Something like info: {history: [{version: 4.0.0, releasedAt: '2050-01-01'}]}. Probably out of scope but just putting here while I'm at it 😅

Should we keep this here, or should I open a distinct issue for the sunset field.

@philsturgeon
Copy link
Contributor

Could you help me with a use-case? What is the benefit of having sunset (a runtime HTTP header that talks about removal) in OpenAPI specifically?

You can already use deprecated: true to say "This is going away soon" and then the implementation is going to give you more specifics through the Deprecation and/or Sunset headers. The implementation may move the date back if not enough folks are doing it, and in that case deprecated: true is still accurate.

I'm just not sure I see the value personally. My brain runs on use-cases so if you could talk through a concrete instance where this would help I could come around?

What I like specifically about the "replacementOperationId" : "getFoo2" idea is that its basically adding onto deprecated: true and saying "aaaaand you should use this instead" which is handy, but it's not trying to get involved with a timeline in any way.

Maybe we do need a new issue so we don't overlap the ideas.

@karenetheridge
Copy link
Member

karenetheridge commented May 12, 2022

(Side note: https://datatracker.ietf.org/doc/html/rfc8594 specifies that the Sunset header contains a date, not a version.)

Could you help me with a use-case? What is the benefit of having sunset (a runtime HTTP header that talks about removal) in OpenAPI specifically?

I would use this to have my HTTP server automatically inject a Sunset header into the response when it sees this in the openapi document.

Another way to do it would be to tailor the application to look for this in the operation spec:

responses:
  200:
    headers:
      Sunset:
        description: injected into the response by the application
        required: true
        schema:
          const: 2022-12-31 00:00:00

..and inject the header based on that, but it's more verbose.

(An openapi validator could also warn if the sunset date is in the past, indicating that the endpoint should be removed entirely.)

@yjaaidi
Copy link

yjaaidi commented May 12, 2022

Could you help me with a use-case? What is the benefit of having sunset (a runtime HTTP header that talks about removal) in OpenAPI specifically?

You can already use deprecated: true to say "This is going away soon" and then the implementation is going to give you more specifics through the Deprecation and/or Sunset headers. The implementation may move the date back if not enough folks are doing it, and in that case deprecated: true is still accurate.

I'm just not sure I see the value personally. My brain runs on use-cases so if you could talk through a concrete instance where this would help I could come around?

What I like specifically about the "replacementOperationId" : "getFoo2" idea is that its basically adding onto deprecated: true and saying "aaaaand you should use this instead" which is handy, but it's not trying to get involved with a timeline in any way.

Maybe we do need a new issue so we don't overlap the ideas.

Thanks @philsturgeon for your quick reply.

Here is the use case, say I've deprecated a couple of things in my specification: title field is deprecated in favor of name and route GET /something is deprecated.
There's no technical way of expressing the emergency of replacing title and getting rid foGET /something.
Using a new sunset property, I can explicitly tell that title will be definitely removed in version 3.0 while GET /something will be removed in version 4.0.

The benefits of this approach are that:

  1. consumers can prioritize their changes
  2. using linters and spec validators, API producers will never forget to remove deprecated stuff from their specs. When version will be bumped to 3.0, I'll get an error if I forget to remove the title field, and on version 4.0, I'll get an error for GET /something
  3. it makes it easier to automate the backend and generate the Sunset header. If I can tell some middleware in my framework that 3.0 will be released on 2023-01-01 and 4.0 on 2023-07-01, the Sunset header could be added automatically based on that information (e.g. oh! you're calling GET /something but it is deprecated and planned to be sunseted on 3.0 which is planned for 2023-01-01 so let me set the Sunset header to 2023-01-1.

@tompahoward
Copy link

Instead of "replacementOperationId" : "getFoo2", I would prefer

deprecatedBy:
    - $ref: '#/paths/~1foo2/get'

or something like that. Using an array supports situations where one endpoint is split into two. By using JSON ref or similar, we provide a resolvable link to the new endpoint(s)

@handrews handrews added the versioning describing versions of APIs/endpoints/operations label Jan 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
versioning describing versions of APIs/endpoints/operations
Projects
None yet
Development

No branches or pull requests