Rules and severities

apigenie diff rates every change between two versions of a document with a rule: the kind of node the change is in, the property that changed, and whether the property was added, removed or modified. Without a configuration, the rules are the recommended ruleset of the standard, listed below for each of them; a configuration that names recommended in extends changes some of them. This page says what the severities mean, how a report names the rule that rated a change, and what makes a breaking change acceptable.

Severities

Severity In reports What it means
breaking breaking A client built against the old version fails: an operation it calls is gone, a parameter it does not send is required, a value it reads has another type.
non_breaking non-breaking Old clients keep working; the change is there for new ones: a new operation, a new optional parameter, a new property in a response.
non_functional non-functional Nothing a client sends or receives changes: a description, a summary, an example, a tag.
none none The change does not count. The status and the changelog leave it out; the detailed report lists it.
nested The property is an object or a list compared member by member, and the change takes the highest severity found inside: info: nested rates a changed info by its title, version and contact.

The status of a comparison is the highest severity of its changes. The command exits with status 1 when it is breaking, so a CI job fails on the pull request that would break a client, and with 0 otherwise. --update-version turns the status into a version: a new major version for breaking, minor for non_breaking, patch for non_functional.

How a rule is written

A rule names a node type, one of its properties, and the severity of a change of that property. The property takes one of these forms:

  • A severity alone, host: breaking, for a property a node always has, which can only be modified, and for a property whose addition, removal and modification weigh the same.
  • add, remove and modify, each with a severity, for an optional property: license: {add: breaking, remove: non_breaking, modify: nested}.
  • add_item, remove_item and modify_item for the items of a list or a map, such as the operations of a path item or the properties of a schema. add_first_item rates the first item of a list that was empty, and remove_last_item the last item of a list that becomes empty: the first value of an enum restricts what was unrestricted, and removing the last one lifts the restriction.
  • A condition in place of a severity: {if: CONDITION, then: SEVERITY, else: SEVERITY}, where the conditions are the ones below.

The defaults node type sets what is not named elsewhere: defaults.extensions rates every x- extension of an OpenAPI, Swagger 2.0 or AsyncAPI document that no node type’s extensions map names, defaults.options the Protobuf options no node type’s options map names, and defaults.directives the GraphQL directives no node kind’s directives map names.

Conditions

Condition Standards Holds when
required OpenAPI, Swagger 2.0, AsyncAPI, GraphQL The added or removed item is required: a parameter with required: true, a property listed in the schema’s required, a GraphQL argument or input field of a non-null type without a default value.
deprecated OpenAPI, Swagger 2.0, Protobuf, GraphQL The item is deprecated: deprecated: true on an operation, a parameter or a schema, an enum value listed in the schema’s x-deprecated-enum, the deprecated = true option of a Protobuf field or enum value, @deprecated in GraphQL.
sunset_passed OpenAPI, Swagger 2.0 The item is deprecated and its x-sunset date is on or before the date of the comparison, the current date or --today.
reserved Protobuf The number of the removed field or enum value is reserved in the new file.
wire_compatible Protobuf The old and the new type of a field are encoded the same way: int32 and int64, string and bytes, a field with and without optional.
compatible GraphQL The old and the new type are compatible for the direction of the data: a field may become non-null and an argument or input field nullable, a list stays a list.
true, false all The new value of a boolean property, such as repeatable of a directive or a Protobuf option; the old value when the property was removed.

Direction

A schema change weighs differently for the data clients send and the data they receive: a new required property breaks the clients that send the object and not those that receive it, and a removed property the other way round. The rules of a standard therefore come in layers. For OpenAPI and Swagger 2.0, rules applies everywhere, request replaces some of them for parameters and request bodies, and response for responses; inside callbacks and webhooks the directions are swapped. A component schema takes the direction of the places it is used in, following the references between schemas in either document; one used both ways, or nowhere, gets the stricter severity of the two.

For AsyncAPI the directions are those of the application the document describes: receive applies to the messages of receive operations, which other parties send, and send to the messages of send operations, which other parties receive. Replies flow the other way than the messages of their operation, and a message used both ways, or by no operation, gets the stricter severity. Protobuf files and GraphQL schemas have no direction layers: a GraphQL argument or input field is always sent and a field always received, and the rules of each node kind say so.

Reading a report

The detailed report (--report detailed) lists every change with the node type and the property that rated it, and the severity the rule gave:

streetlights.yaml
   38   5        breaking  Removed asyncapi.servers['mtls-connections']
  128   7    non-breaking  Added channel.messages['dimLight']
  167   7  non-functional  Added schema.description

asyncapi.servers['mtls-connections'] is a removed item of the servers property of the asyncapi node: the rule is asyncapi.servers.remove_item, breaking in the AsyncAPI table below. A report spells a node type of two words with an underscore, security_scheme or path_item, where the configuration and the tables write securityScheme and pathItem. The JSON report carries the same two names in target and config_key. The changelog report, the default, groups the changes by operation, channel, message or type and lists the rated changes of each one under it, with the same words.

Accepting a breaking change

A failed check leaves four ways forward, the first two of them without touching the rules.

Announce the removal first. In OpenAPI and Swagger 2.0 documents, the recommended rules let a deprecated operation, path item, parameter, component schema, response property or request enum value go once its x-sunset date has passed, the sunset_passed condition. Mark it in one pull request, which is non_breaking, and remove it in another after the date:

paths:
  /v1/orders:
    get:
      deprecated: true
      x-sunset: 2027-01-31

x-sunset is an RFC 3339 date or date-time next to deprecated: true, on the element itself, next to the $ref that uses it, or where it is defined; an enum value is deprecated when the schema’s x-deprecated-enum lists it, and goes on the schema’s x-sunset date. --today runs the comparison as of another date, to see the removal pass before then. apigenie evolve plan writes the deprecations and the removal in two waves.

Reserve the number. In a Protobuf file, a removed field or enum value whose number is reserved is non_breaking, the reserved condition: the removal is deliberate, and no later field can take the number and misread the data of the old one.

message Customer {
  reserved 7;
  reserved "telex";
  string name = 1;
}

Change the rule. When a change is breaking by the rules and not for the API’s clients, say so in the commands.diff block of the configuration, starting from recommended and overriding the rule, the same path as in the tables below. overrides limits a rule to the locations that match a pattern, and none turns a change off:

version: '1.0'
commands:
  diff:
    extends: [recommended]
    rules:
      operation:
        # tags are not part of the contract of this API
        tags: none
    graphql:
      extends: [recommended]
      rules:
        object:
          fields:
            # fields marked @deprecated may go
            remove_item:
              if: deprecated
              then: non_breaking
              else: breaking
    overrides:
      - where:
          location: /paths/~1internal~1*/**
        rules:
          pathItem:
            operations:
              remove_item: non_breaking

The sections diff, diff.swagger, diff.asyncapi, diff.protobuf and diff.graphql of the diff reference describe the keys of each standard and the location patterns.

Ship a new major version. When the break is intended, the check records it rather than blocks it: --update-version writes the major version the changes call for into the document, and the GitHub Action with fail-on: never, or a job that ignores exit status 1, reports the changes without failing.

The tables list the recommended ruleset of each standard as the CLI of this release applies it: the node type, the property, the rule in the form a configuration writes it, and the reason the ruleset gives. A standard with directions has a table per direction after its rules.

OpenAPI

Node type Property Rule Note
defaults extensions non_functional
openapi openapi breaking
info nested
jsonSchemaDialect none
servers add_item: non_breaking, remove_item: breaking, modify_item: nested
paths nested
webhooks add_item: non_breaking, remove_item: breaking, modify_item: nested
components nested
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested add_item: Requirements are alternatives: adding one to existing ones is harmless, but requiring authentication where none was required breaks clients.
tags add_item: non_breaking, remove_item: breaking, modify_item: nested
externalDocs add: non_breaking, remove: non_breaking, modify: nested
info title breaking
summary non_functional
description non_functional
termsOfService breaking
contact nested
license add: breaking, remove: non_breaking, modify: nested
version none
contact name add: non_breaking, remove: breaking, modify: non_breaking
url add: non_breaking, remove: breaking, modify: non_breaking
email add: non_breaking, remove: breaking, modify: non_breaking
license name breaking
identifier breaking
url breaking
server url breaking
description non_functional
variables add_item: breaking, remove_item: breaking, modify_item: nested
serverVariable description non_functional
default breaking
enum add_item: non_breaking, remove_item: breaking
components schemas add_item: non_breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, modify_item: nested remove_item: A deprecated schema may go once its x-sunset date has passed.
responses add_item: non_breaking, remove_item: breaking, modify_item: nested
parameters add_item: non_breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, modify_item: nested
examples non_functional
requestBodies add_item: non_breaking, remove_item: breaking, modify_item: nested
headers add_item: non_breaking, remove_item: breaking, modify_item: nested
securitySchemes add_item: non_breaking, remove_item: breaking, modify_item: nested
links add_item: non_breaking, remove_item: breaking, modify_item: nested
callbacks add_item: non_breaking, remove_item: breaking, modify_item: nested
pathItems add_item: non_breaking, remove_item: breaking, modify_item: nested
paths add_item: non_breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, modify_item: nested remove_item: A path whose operations are all deprecated may go once their sunset dates have passed.
pathItem summary non_functional
description non_functional
parameters add_item: {if: required, then: breaking, else: non_breaking}, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, modify_item: nested remove_item: A deprecated parameter may go once its x-sunset date has passed.
operations add_item: non_breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, modify_item: nested remove_item: A deprecated operation may go once its x-sunset date has passed; to allow removing every deprecated operation, use remove_item: { if: deprecated, then: non_breaking, else: breaking }.
servers add_item: non_breaking, remove_item: breaking, modify_item: nested
operation tags non_breaking
summary non_functional
operationId breaking
description non_functional
externalDocs non_functional
deprecated non_breaking Deprecating announces a removal; the removal itself is the breaking change.
parameters add_item: {if: required, then: breaking, else: non_breaking}, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, modify_item: nested remove_item: A deprecated parameter may go once its x-sunset date has passed.
requestBody nested
responses nested
callbacks add_item: breaking, remove_item: breaking, modify_item: nested
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested
servers add_item: non_breaking, remove_item: breaking, modify_item: nested
externalDocumentation description non_functional
url non_functional
parameter name breaking
in breaking
description non_functional
required modify: {if: true, then: breaking, else: non_breaking}
deprecated non_breaking
allowEmptyValue modify: {if: true, then: non_breaking, else: breaking}
style breaking
explode breaking
allowReserved modify: {if: true, then: non_breaking, else: breaking}
schema add: breaking, remove: breaking, modify: nested
content add_item: breaking, remove_item: breaking, modify_item: nested
example non_functional
examples non_functional
requestBody description non_functional
required add: {if: true, then: breaking, else: non_breaking}, remove: breaking, modify: {if: true, then: breaking, else: non_breaking}
content add_item: non_breaking, remove_item: breaking, modify_item: nested
mediaType schema add: breaking, remove: breaking, modify: nested
encoding add_item: breaking, remove_item: breaking, modify_item: nested
example non_functional
examples non_functional
encoding contentType breaking
headers add_item: non_breaking, remove_item: breaking, modify_item: nested
style breaking
explode breaking
allowReserved breaking
responses add_item: breaking, remove_item: breaking, modify_item: nested
response description non_functional
headers add_item: breaking, remove_item: breaking, modify_item: nested
content add_item: breaking, remove_item: breaking, modify_item: nested
links add_item: non_breaking, remove_item: breaking, modify_item: nested
callback add_item: breaking, remove_item: breaking, modify_item: nested
example summary non_functional
description non_functional
value non_functional
externalValue non_functional
link description non_functional
server non_breaking
operationRef non_breaking
operationId non_breaking
parameters non_breaking
requestBody non_breaking
header name breaking
description non_functional
required modify: {if: true, then: breaking, else: non_breaking}
deprecated non_breaking
allowEmptyValue breaking
explode breaking
allowReserved breaking
schema add: breaking, remove: breaking, modify: nested
content add_item: breaking, remove_item: breaking, modify_item: nested
example non_functional
examples non_functional
tag name breaking
description non_functional
externalDocs non_functional
reference $ref breaking
summary non_breaking
description non_functional
schema id breaking
title non_breaking
description non_functional
type breaking
format breaking
const breaking
enum add_item: breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking} remove_item: A value listed in x-deprecated-enum may go once the schema’s x-sunset date has passed.
deprecated non_breaking
readOnly non_functional
writeOnly non_functional
minimum breaking
exclusiveMinimum breaking
maximum breaking
exclusiveMaximum breaking
multipleOf breaking
minLength breaking
maxLength breaking
pattern breaking
items add: breaking, remove: non_breaking, modify: nested
contains add: breaking, remove: breaking, modify: nested
minContains breaking
maxContains breaking
prefixItems breaking
minItems breaking
maxItems breaking
uniqueItems breaking
unevaluatedItems add: breaking, remove: breaking, modify: nested
required breaking
properties add_item: non_breaking, remove_item: non_breaking, modify_item: nested
patternProperties add_item: non_breaking, remove_item: non_breaking, modify_item: nested
propertyNames non_breaking
additionalProperties breaking
minProperties breaking
maxProperties breaking
unevaluatedProperties add: breaking, remove: breaking, modify: nested
allOf add_item: breaking, remove_item: breaking, modify_item: nested
oneOf add_item: breaking, remove_item: breaking, modify_item: nested
anyOf add_item: breaking, remove_item: breaking, modify_item: nested
not add: breaking, remove: breaking, modify: nested
if add: breaking, remove: breaking, modify: nested
then add: breaking, remove: breaking, modify: nested
else add: breaking, remove: breaking, modify: nested
anchor none
dynamicAnchor none
dynamicRef none
vocabulary breaking
contentEncoding breaking
contentMediaType breaking
contentSchema nested
discriminator add: breaking, remove: non_breaking, modify: nested
xml nested
externalDocs non_functional
examples non_functional
default breaking
dialect breaking
discriminator propertyName breaking
mapping breaking
xml name breaking
namespace breaking
prefix breaking
attribute breaking
wrapped breaking
securityScheme name breaking
description non_functional
type breaking
in breaking
scheme breaking
bearerFormat breaking
flows add: breaking, remove: breaking, modify: nested
openIdConnectUrl breaking
oauthFlows implicit add: breaking, remove: breaking, modify: nested
password add: breaking, remove: breaking, modify: nested
clientCredentials add: breaking, remove: breaking, modify: nested
authorizationCode add: breaking, remove: breaking, modify: nested
oauthFlow authorizationUrl breaking
tokenUrl breaking
refreshUrl breaking
scopes add_item: breaking, remove_item: breaking, modify_item: non_functional
securityRequirement breaking

Severities that depend on the direction of data flow. They override rules in requests (parameters, request bodies; sent by API consumers) and responses (sent by API providers). Inside callbacks and webhooks the directions are swapped, as the API provider sends the requests there. Changes in places shared by both (e.g. component schemas) get the stricter of the request and the response severity.

These defaults assume that servers and clients ignore unknown properties. For clients that reject them, set response.schema.properties.add_item to breaking.

The request layer, for the parameters and the request bodies, replaces these rules of the layer above:

Node type Property Rule Note
schema properties add_item: {if: required, then: breaking, else: non_breaking}, remove_item: non_breaking add_item: Existing clients don’t send new required properties.
required add_item: breaking, remove_item: non_breaking
enum add_item: non_breaking, add_first_item: breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking}, remove_last_item: non_breaking add_first_item: Restricting a value to a set of values. remove_item: A value listed in x-deprecated-enum may go once the schema’s x-sunset date has passed.

The response layer, for the responses, replaces these:

Node type Property Rule Note
schema properties add_item: non_breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking} remove_item: Existing clients may rely on the removed property, unless it was deprecated and its x-sunset date has passed.
required add_item: non_breaking, remove_item: {if: sunset_passed, then: non_breaking, else: breaking} remove_item: The item of a deprecated property goes with the property, once its x-sunset date has passed.
enum add_item: breaking, add_first_item: non_breaking, remove_item: non_breaking, remove_last_item: breaking add_item: Existing clients may not handle new values.

Swagger 2.0

The recommended severities of changes of Swagger 2.0 documents. A severity is one of none (not reported), non_functional, non_breaking, breaking and nested (the highest severity of the nested changes).

Node type Property Rule Note
defaults extensions non_functional
swagger swagger breaking
info nested
host breaking Where the API is served.
basePath breaking
schemes add_item: non_breaking, remove_item: breaking
consumes add_item: non_breaking, remove_item: breaking Media types the API accepts; clients may send the removed ones.
produces add_item: breaking, remove_item: non_breaking Media types the API returns; clients may not accept the new ones.
paths add_item: non_breaking, remove_item: breaking, modify_item: nested
definitions add_item: non_breaking, remove_item: breaking, modify_item: nested
parameters add_item: non_breaking, remove_item: breaking, modify_item: nested
responses add_item: non_breaking, remove_item: breaking, modify_item: nested
securityDefinitions add_item: non_breaking, remove_item: breaking, modify_item: nested
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested add_item: Requirements are alternatives: adding one to existing ones is harmless, but requiring authentication where none was required breaks clients.
tags add_item: non_breaking, remove_item: breaking, modify_item: nested
externalDocs add: non_breaking, remove: non_breaking, modify: nested
info title breaking
description non_functional
termsOfService breaking
contact nested
license add: breaking, remove: non_breaking, modify: nested
version none
contact name add: non_breaking, remove: breaking, modify: non_breaking
url add: non_breaking, remove: breaking, modify: non_breaking
email add: non_breaking, remove: breaking, modify: non_breaking
license name breaking
url breaking
pathItem $ref breaking
parameters add_item: {if: required, then: breaking, else: non_breaking}, remove_item: breaking, modify_item: nested
operations add_item: non_breaking, remove_item: breaking, modify_item: nested remove_item: To allow removing deprecated operations, use remove_item: { if: deprecated, then: non_breaking, else: breaking }.
operation tags non_breaking
summary non_functional
description non_functional
externalDocs non_functional
operationId breaking
consumes add_item: non_breaking, remove_item: breaking
produces add_item: breaking, remove_item: non_breaking
parameters add_item: {if: required, then: breaking, else: non_breaking}, remove_item: breaking, modify_item: nested
responses add_item: breaking, remove_item: breaking, modify_item: nested
schemes add_item: non_breaking, remove_item: breaking
deprecated modify: {if: true, then: breaking, else: non_breaking}
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested
parameter name breaking
in breaking
description non_functional
required modify: {if: true, then: breaking, else: non_breaking}
schema add: breaking, remove: breaking, modify: nested The schema of a body parameter.
type breaking
format breaking
allowEmptyValue modify: {if: true, then: non_breaking, else: breaking}
items add: breaking, remove: breaking, modify: nested
collectionFormat breaking
default breaking
multipleOf breaking
maximum breaking
exclusiveMaximum breaking
minimum breaking
exclusiveMinimum breaking
maxLength breaking
minLength breaking
pattern breaking
maxItems breaking
minItems breaking
uniqueItems breaking
enum add_item: breaking, remove_item: breaking
response description non_functional
schema add: breaking, remove: breaking, modify: nested
headers add_item: breaking, remove_item: breaking, modify_item: nested
examples non_functional
header description non_functional
type breaking
format breaking
items add: breaking, remove: breaking, modify: nested
collectionFormat breaking
default breaking
multipleOf breaking
maximum breaking
exclusiveMaximum breaking
minimum breaking
exclusiveMinimum breaking
maxLength breaking
minLength breaking
pattern breaking
maxItems breaking
minItems breaking
uniqueItems breaking
enum add_item: breaking, remove_item: breaking
items type breaking
format breaking
items add: breaking, remove: breaking, modify: nested
collectionFormat breaking
default breaking
multipleOf breaking
maximum breaking
exclusiveMaximum breaking
minimum breaking
exclusiveMinimum breaking
maxLength breaking
minLength breaking
pattern breaking
maxItems breaking
minItems breaking
uniqueItems breaking
enum add_item: breaking, remove_item: breaking
schema title non_breaking
description non_functional
type breaking
format breaking
default breaking
multipleOf breaking
maximum breaking
exclusiveMaximum breaking
minimum breaking
exclusiveMinimum breaking
maxLength breaking
minLength breaking
pattern breaking
maxItems breaking
minItems breaking
uniqueItems breaking
enum add_item: breaking, remove_item: breaking
items add: breaking, remove: non_breaking, modify: nested
allOf add_item: breaking, remove_item: breaking, modify_item: nested
properties add_item: non_breaking, remove_item: non_breaking, modify_item: nested
required breaking
maxProperties breaking
minProperties breaking
additionalProperties add: breaking, remove: breaking, modify: nested add when additional properties become allowed, remove when they become forbidden, modify for changes of the schema of additional properties.
discriminator breaking
readOnly non_functional
xml nested
externalDocs non_functional
example non_functional
xml name breaking
namespace breaking
prefix breaking
attribute breaking
wrapped breaking
securityScheme type breaking
description non_functional
name breaking
in breaking
flow breaking
authorizationUrl breaking
tokenUrl breaking
scopes add_item: breaking, remove_item: breaking, modify_item: non_functional
securityRequirement scopes add_item: breaking, remove_item: non_breaking Clients may not have tokens with the new scopes.
tag name breaking
description non_functional
externalDocs non_functional
externalDocumentation description non_functional
url non_functional

Severities that depend on the direction of data flow. They override rules in requests (parameters, including body parameters; sent by API clients) and responses (sent by the API). Changes in places shared by both (e.g. definitions) get the stricter of the request and the response severity.

These defaults assume that servers and clients ignore unknown properties. For clients that reject them, set response.schema.properties.add_item to breaking.

The request layer, for the parameters, replaces these rules of the layer above:

Node type Property Rule Note
schema properties add_item: {if: required, then: breaking, else: non_breaking}, remove_item: non_breaking add_item: Existing clients don’t send new required properties.
required add_item: breaking, remove_item: non_breaking
enum add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking add_first_item: Restricting a value to a set of values.
additionalProperties add: non_breaking, remove: breaking
parameter enum add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking
items enum add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking

The response layer, for the responses, replaces these:

Node type Property Rule Note
schema properties add_item: non_breaking, remove_item: breaking remove_item: Existing clients may rely on the removed property.
required add_item: non_breaking, remove_item: breaking
enum add_item: breaking, add_first_item: non_breaking, remove_item: non_breaking, remove_last_item: breaking add_item: Existing clients may not handle new values.
additionalProperties add: non_breaking, remove: non_breaking
header enum add_item: breaking, add_first_item: non_breaking, remove_item: non_breaking, remove_last_item: breaking
items enum add_item: breaking, add_first_item: non_breaking, remove_item: non_breaking, remove_last_item: breaking

AsyncAPI

Node type Property Rule Note
defaults extensions non_functional
asyncapi asyncapi non_breaking The specification version; minor versions of AsyncAPI 3 are compatible.
id breaking
info nested
servers add_item: non_breaking, remove_item: breaking, modify_item: nested
defaultContentType breaking
channels add_item: non_breaking, remove_item: breaking, modify_item: nested
operations add_item: non_breaking, remove_item: breaking, modify_item: nested
components nested
info title non_functional
version none
description non_functional
termsOfService non_breaking
contact nested
license add: breaking, remove: non_breaking, modify: nested
tags non_functional
externalDocs non_functional
contact name non_functional
url non_functional
email non_functional
license name breaking
url breaking
server host breaking
protocol breaking
protocolVersion breaking
pathname breaking
description non_functional
title non_functional
summary non_functional
variables add_item: breaking, remove_item: breaking, modify_item: nested
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested add_item: Security schemes are alternatives: adding one to existing ones is harmless, but requiring authentication where none was required breaks clients.
tags non_functional
externalDocs non_functional
bindings nested
serverVariable enum add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking
default breaking
description non_functional
examples non_functional
channel address breaking
messages add_item: non_breaking, remove_item: breaking, modify_item: nested
title non_functional
summary non_functional
description non_functional
servers add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking add_item: A channel without servers is available on all servers.
parameters add_item: breaking, remove_item: breaking, modify_item: nested
tags non_functional
externalDocs non_functional
bindings nested
parameter enum add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking
default breaking
description non_functional
examples non_functional
location breaking
operation action breaking
channel breaking
title non_functional
summary non_functional
description non_functional
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested
tags non_functional
externalDocs non_functional
bindings nested
traits nested Traits are applied to the operation, so their changes are the operation’s changes.
messages add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking add_item: An operation without messages uses all messages of its channel.
reply add: breaking, remove: breaking, modify: nested
operationTrait title non_functional
summary non_functional
description non_functional
security add_item: non_breaking, add_first_item: breaking, remove_item: breaking, modify_item: nested
tags non_functional
externalDocs non_functional
bindings nested
operationReply address nested
channel breaking
messages add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking
operationReplyAddress description non_functional
location breaking
message headers add: breaking, remove: breaking, modify: nested
payload add: breaking, remove: breaking, modify: nested
correlationId add: non_breaking, remove: breaking, modify: nested
contentType breaking
name non_breaking
title non_functional
summary non_functional
description non_functional
tags non_functional
externalDocs non_functional
bindings nested
examples non_functional
traits nested Traits are applied to the message, so their changes are the message’s changes.
messageTrait headers add: breaking, remove: breaking, modify: nested
correlationId add: non_breaking, remove: breaking, modify: nested
contentType breaking
name non_breaking
title non_functional
summary non_functional
description non_functional
tags non_functional
externalDocs non_functional
bindings nested
examples non_functional
messageExample headers non_functional
payload non_functional
name non_functional
summary non_functional
correlationId description non_functional
location breaking
tag name non_functional
description non_functional
externalDocs non_functional
externalDocumentation description non_functional
url non_functional
components schemas add_item: non_breaking, remove_item: breaking, modify_item: nested
servers add_item: non_breaking, remove_item: breaking, modify_item: nested
channels add_item: non_breaking, remove_item: breaking, modify_item: nested
operations add_item: non_breaking, remove_item: breaking, modify_item: nested
messages add_item: non_breaking, remove_item: breaking, modify_item: nested
securitySchemes add_item: non_breaking, remove_item: breaking, modify_item: nested
serverVariables add_item: non_breaking, remove_item: breaking, modify_item: nested
parameters add_item: non_breaking, remove_item: breaking, modify_item: nested
correlationIds add_item: non_breaking, remove_item: breaking, modify_item: nested
replies add_item: non_breaking, remove_item: breaking, modify_item: nested
replyAddresses add_item: non_breaking, remove_item: breaking, modify_item: nested
externalDocs non_functional
tags non_functional
operationTraits add_item: non_breaking, remove_item: breaking, modify_item: nested
messageTraits add_item: non_breaking, remove_item: breaking, modify_item: nested
serverBindings add_item: non_breaking, remove_item: breaking, modify_item: nested
channelBindings add_item: non_breaking, remove_item: breaking, modify_item: nested
operationBindings add_item: non_breaking, remove_item: breaking, modify_item: nested
messageBindings add_item: non_breaking, remove_item: breaking, modify_item: nested
schema id breaking
dialect breaking
comment none
title non_functional
description non_functional
type breaking
enum add_item: breaking, remove_item: breaking
const breaking
default breaking
examples non_functional
readOnly non_functional
writeOnly non_functional
deprecated non_breaking
format breaking
contentEncoding breaking
contentMediaType breaking
multipleOf breaking
maximum breaking
exclusiveMaximum breaking
minimum breaking
exclusiveMinimum breaking
maxLength breaking
minLength breaking
pattern breaking
items add: breaking, remove: non_breaking, modify: nested
tupleItems add_item: breaking, remove_item: breaking, modify_item: nested
additionalItems add: breaking, remove: breaking, modify: nested
maxItems breaking
minItems breaking
uniqueItems breaking
contains add: breaking, remove: breaking, modify: nested
maxProperties breaking
minProperties breaking
required breaking
properties add_item: non_breaking, remove_item: non_breaking, modify_item: nested
patternProperties add_item: non_breaking, remove_item: non_breaking, modify_item: nested
additionalProperties breaking
dependencies breaking
propertyNames non_breaking
if add: breaking, remove: breaking, modify: nested
then add: breaking, remove: breaking, modify: nested
else add: breaking, remove: breaking, modify: nested
allOf add_item: breaking, remove_item: breaking, modify_item: nested
anyOf add_item: breaking, remove_item: breaking, modify_item: nested
oneOf add_item: breaking, remove_item: breaking, modify_item: nested
not add: breaking, remove: breaking, modify: nested
discriminator breaking
externalDocs non_functional
multiFormatSchema schemaFormat breaking
schema nested
untypedSchema breaking Schemas in formats without a typed model, e.g. Avro, are compared as whole values.
reference ref breaking A reference the document can’t follow (to another file or a URL) is compared by its $ref: another one may lead to anything.
securityScheme type breaking
description non_functional
name breaking
in breaking
scheme breaking
bearerFormat breaking
flows add: breaking, remove: breaking, modify: nested
openIdConnectUrl breaking
scopes add_item: breaking, remove_item: non_breaking add_item: The scopes a server or an operation requires.
oauthFlows implicit add: breaking, remove: breaking, modify: nested
password add: breaking, remove: breaking, modify: nested
clientCredentials add: breaking, remove: breaking, modify: nested
authorizationCode add: breaking, remove: breaking, modify: nested
oauthFlow authorizationUrl breaking
tokenUrl breaking
refreshUrl breaking
availableScopes add_item: breaking, remove_item: breaking, modify_item: non_functional
serverBindings protocols add_item: breaking, remove_item: breaking, modify_item: nested
untyped breaking Bindings of protocols without a typed model are compared as a whole.
channelBindings protocols add_item: breaking, remove_item: breaking, modify_item: nested
untyped breaking
operationBindings protocols add_item: breaking, remove_item: breaking, modify_item: nested
untyped breaking
messageBindings protocols add_item: breaking, remove_item: breaking, modify_item: nested
untyped breaking
httpOperationBinding bindingVersion non_breaking
method breaking
query add: breaking, remove: breaking, modify: nested
httpMessageBinding bindingVersion non_breaking
headers add: breaking, remove: breaking, modify: nested
statusCode breaking
webSocketsChannelBinding bindingVersion non_breaking
method breaking
query add: breaking, remove: breaking, modify: nested
headers add: breaking, remove: breaking, modify: nested
kafkaServerBinding bindingVersion non_breaking
schemaRegistryUrl breaking
schemaRegistryVendor breaking
kafkaChannelBinding bindingVersion non_breaking
topic breaking
partitions breaking
replicas breaking
topicConfiguration add: breaking, remove: breaking, modify: nested
kafkaTopicConfiguration cleanupPolicy breaking
retentionMs breaking
retentionBytes breaking
deleteRetentionMs breaking
maxMessageBytes breaking
confluentKeySchemaValidation breaking
confluentKeySubjectNameStrategy breaking
confluentValueSchemaValidation breaking
confluentValueSubjectNameStrategy breaking
kafkaOperationBinding bindingVersion non_breaking
groupId add: breaking, remove: breaking, modify: nested
clientId add: breaking, remove: breaking, modify: nested
kafkaMessageBinding bindingVersion non_breaking
key add: breaking, remove: breaking, modify: nested
schemaIdLocation breaking
schemaIdPayloadEncoding breaking
schemaLookupStrategy breaking
amqpChannelBinding bindingVersion non_breaking
is breaking
exchange add: breaking, remove: breaking, modify: nested
queue add: breaking, remove: breaking, modify: nested
amqpExchange name breaking
type breaking
durable breaking
autoDelete breaking
vhost breaking
amqpQueue name breaking
durable breaking
exclusive breaking
autoDelete breaking
vhost breaking
amqpOperationBinding bindingVersion non_breaking
expiration breaking
userId breaking
cc breaking
priority breaking
deliveryMode breaking
mandatory breaking
bcc breaking
timestamp breaking
ack breaking
amqpMessageBinding bindingVersion non_breaking
contentEncoding breaking
messageType breaking
mqttServerBinding bindingVersion non_breaking
clientId breaking
cleanSession breaking
lastWill add: breaking, remove: breaking, modify: nested
keepAlive breaking
sessionExpiryInterval breaking
maximumPacketSize breaking
mqttLastWill topic breaking
qos breaking
message breaking
retain breaking
mqttOperationBinding bindingVersion non_breaking
qos breaking
retain breaking
messageExpiryInterval breaking
mqttMessageBinding bindingVersion non_breaking
payloadFormatIndicator breaking
correlationData add: breaking, remove: breaking, modify: nested
contentType breaking
responseTopic breaking

Severities that depend on the direction in which messages flow, from the point of view of the application the document describes. They override rules for messages the application receives (used by receive operations; sent by other parties) and for messages it sends (used by send operations; received by other parties). Replies flow the other way than the messages of their operation. Messages used in both directions, or by no operation (e.g. component messages), get the stricter of the receive and the send severity.

These defaults assume that applications ignore unknown message properties. For receivers that reject them, set send.schema.properties.add_item to breaking.

The receive layer, for the messages of receive operations, replaces these rules of the layer above:

Node type Property Rule Note
message correlationId add: breaking add: Senders don’t provide the new correlation ID yet.
schema properties add_item: {if: required, then: breaking, else: non_breaking}, remove_item: non_breaking add_item: Existing senders don’t send new required properties.
required add_item: breaking, remove_item: non_breaking
enum add_item: non_breaking, add_first_item: breaking, remove_item: breaking, remove_last_item: non_breaking add_first_item: Restricting a value to a set of values.

The send layer, for the messages of send operations, replaces these:

Node type Property Rule Note
message correlationId add: non_breaking
schema properties add_item: non_breaking, remove_item: breaking remove_item: Existing receivers may rely on the removed property.
required add_item: non_breaking, remove_item: breaking
enum add_item: breaking, add_first_item: non_breaking, remove_item: non_breaking, remove_last_item: breaking add_item: Existing receivers may not handle new values.

Protobuf

Severities of changes between two versions of a Protobuf (proto3) file.

A change is breaking when it breaks the programs that use the file: those exchanging encoded messages (the binary or the JSON encoding) and those compiled with the code generated from it.

Node type Property Rule Note
defaults options non_breaking An option may change the behaviour of the generated code or of the tools that read it; options known to break their users are listed per node type below.
file package breaking The package is a part of the names of all types and of the paths of gRPC methods.
imports add_item: non_functional, remove_item: non_functional, modify_item: nested
messages add_item: non_breaking, remove_item: breaking, modify_item: nested
enums add_item: non_breaking, remove_item: breaking, modify_item: nested
services add_item: non_breaking, remove_item: breaking, modify_item: nested
extends add_item: non_breaking, remove_item: breaking, modify_item: nested Extend blocks, by the extended type.
comment non_functional The comment of the syntax statement.
options.cc_enable_arenas breaking These decide the names and the layout of the generated code.
options.cc_generic_services breaking
options.csharp_namespace breaking
options.go_package breaking
options.java_generic_services breaking
options.java_multiple_files breaking
options.java_outer_classname breaking
options.java_package breaking
options.java_string_check_utf8 breaking
options.objc_class_prefix breaking
options.optimize_for breaking
options.php_class_prefix breaking
options.php_generic_services breaking
options.php_metadata_namespace breaking
options.php_namespace breaking
options.py_generic_services breaking
options.ruby_package breaking
options.swift_prefix breaking
import kind breaking Files that import this one see the types of its public imports.
message fields add_item: non_breaking, remove_item: {if: reserved, then: non_breaking, else: breaking}, modify_item: nested remove_item: A removed field breaks the code that uses it, unless its number is reserved: then the removal is deliberate, and the number can’t be taken by another field, which would misread the data of the old one.
oneofs add_item: non_breaking, remove_item: breaking, modify_item: nested
messages add_item: non_breaking, remove_item: breaking, modify_item: nested
enums add_item: non_breaking, remove_item: breaking, modify_item: nested
extends add_item: non_breaking, remove_item: breaking, modify_item: nested
reservedNumbers add_item: non_breaking, remove_item: breaking A number or a name that is no longer reserved can be taken by a new field.
reservedNames add_item: non_breaking, remove_item: breaking
comment non_functional
options.message_set_wire_format breaking
options.no_standard_descriptor_accessor breaking
field name breaking The name is used by the JSON encoding and by the generated code.
type breaking Some types are encoded the same way, e.g. int32 and int64; to accept such changes, use type: { modify: { if: wire_compatible, then: non_breaking, else: breaking } }.
label breaking optional and repeated.
oneof breaking Moving a field into or out of a oneof.
comment non_functional
options.ctype breaking
options.jstype breaking
options.json_name breaking
oneof comment non_functional
enum values add_item: non_breaking, remove_item: {if: reserved, then: non_breaking, else: breaking}, modify_item: nested
reservedNumbers add_item: non_breaking, remove_item: breaking
reservedNames add_item: non_breaking, remove_item: breaking
comment non_functional
enumValue name breaking The name is used by the JSON encoding and by the generated code.
comment non_functional
service methods add_item: non_breaking, remove_item: breaking, modify_item: nested
comment non_functional
method inputType breaking
outputType breaking
clientStreaming breaking
serverStreaming breaking
comment non_functional
options.idempotency_level breaking
options.(google.api.http) breaking The HTTP mapping of the method.
extend fields add_item: non_breaking, remove_item: breaking, modify_item: nested
comment non_functional

GraphQL

Severities of changes between two versions of a GraphQL schema.

A change is breaking when operations that were valid for the old schema become invalid, or when clients can’t read their results any more. The rules follow graphql-js’s findBreakingChanges(); its “dangerous” changes, e.g. a new enum value or a changed default value, are non-breaking here.

Node type Property Rule Note
defaults directives non_breaking Directives are mostly read by servers and tools; those known to change the meaning of the schema for clients are listed per node kind below.
schema query add: non_breaking, remove: breaking, modify: breaking The root operation types.
mutation add: non_breaking, remove: breaking, modify: breaking
subscription add: non_breaking, remove: breaking, modify: breaking
types add_item: non_breaking, remove_item: breaking, modify_item: nested
directiveDefinitions add_item: non_breaking, remove_item: breaking, modify_item: nested
description non_functional
type kind breaking E.g. an object type that becomes an interface.
scalar description non_functional
directives.specifiedBy non_functional
object interfaces add_item: non_breaking, remove_item: breaking Fragments on an interface no longer match the type.
fields add_item: non_breaking, remove_item: breaking, modify_item: nested
description non_functional
interface interfaces add_item: non_breaking, remove_item: breaking
fields add_item: non_breaking, remove_item: breaking, modify_item: nested
description non_functional
union members add_item: non_breaking, remove_item: breaking
description non_functional
enum values add_item: non_breaking, remove_item: breaking, modify_item: nested
description non_functional
enumValue description non_functional
inputObject fields add_item: {if: required, then: breaking, else: non_breaking}, remove_item: breaking, modify_item: nested add_item: Operations that don’t give a value for a new required field become invalid.
description non_functional
directives.oneOf breaking Values of a @oneOf input object have exactly one field.
field type modify: {if: compatible, then: non_breaking, else: breaking} A field may become non-null, but not nullable; lists must stay lists.
arguments add_item: {if: required, then: breaking, else: non_breaking}, remove_item: breaking, modify_item: nested
description non_functional
argument type modify: {if: compatible, then: non_breaking, else: breaking} An argument may become nullable, but not non-null.
defaultValue add: non_breaking, remove: {if: required, then: breaking, else: non_breaking}, modify: non_breaking Operations that relied on the default value of a non-null argument become invalid.
description non_functional
inputField type modify: {if: compatible, then: non_breaking, else: breaking}
defaultValue add: non_breaking, remove: {if: required, then: breaking, else: non_breaking}, modify: non_breaking
description non_functional
directive arguments add_item: {if: required, then: breaking, else: non_breaking}, remove_item: breaking, modify_item: nested
repeatable modify: {if: false, then: breaking, else: non_breaking} Documents that use the directive more than once at a location become invalid.
locations add_item: non_breaking, remove_item: breaking
description non_functional

See also

  • diff: Every option of the command, the node types and keys of each standard, and the JSON report.
  • Catch breaking changes in CI: The check in a pull request, and retiring an operation on a schedule.
  • changelog: The changes between the revisions of a file in git, with the same rules.
Was this helpful?