# Catch breaking changes in CI

Source: https://apigenie.pl/docs/guides/catch-breaking-changes-in-ci

A breaking change is one that makes a working client fail: a removed operation, a new required
parameter, a response field that went away. This guide makes `apigenie diff` stop such changes in
pull requests, shows how to remove things without breaking anyone, and builds a changelog from the
history of the file. It uses an OpenAPI file, `openapi.yaml`; Swagger 2.0, AsyncAPI, Protobuf and
GraphQL files are compared the same way, each by what its clients depend on.

You need the CLI installed; the [Quickstart](/docs/quickstart) shows how.

## Compare two versions

`apigenie diff` takes the version clients use today and the new one:

```sh
git show main:openapi.yaml > main.yaml
apigenie diff main.yaml openapi.yaml
```

Every change is rated **breaking**, **non-breaking** or **non-functional** (a description, an
example). The command exits with status 1 when any change is breaking, so a script or a CI job can
stop there. `--report` picks what it prints: `changelog` (the default), `status` (one word) or
`detailed` (every property that changed); `--formatter` writes it as JSON, Markdown, HTML or a
format editors understand.

Both files must be of the same standard. Here is the report for two versions of a Protobuf file:

```text
shop.proto
  19  1      breaking  Modified message 'Customer'
  19  1  non-breaking    Removed field 'fax'
  25  3      breaking    Modified field 'full_name'
  25  3      breaking      Modified field name
```

## Check every pull request

The [GitHub Action](/docs/ci/github-action) compares each file with its version at the base of the
pull request and shows the changes as annotations:

```yaml
on: pull_request
jobs:
  api:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: mykulyak/api-genie-action@v0
        with:
          command: diff
          files: api/openapi.yaml
```

The step fails on breaking changes. Set `base` to compare with another branch or tag, such as the
last release, and `fail-on: never` to report without failing.

A failing step only blocks the merge once the check is required. In the repository's settings,
under *Branches*, add a branch protection rule for the base branch (or a ruleset under *Rules*),
turn on *Require status checks to pass before merging* and pick the `api` job. From then on a pull
request with a breaking change waits until the change is withdrawn, or its severity is
[reconsidered](#decide-what-breaks-your-clients).

## Decide what breaks your clients

The `recommended` rules fit most APIs. When yours differ, change them in `apigenie.yaml`: each
property of each kind of node takes a severity (`breaking`, `non_breaking`, `non_functional` or
`none`), or one each for `add`, `remove` and `modify`. List `recommended` in `extends` to change only
some of its rules:

```yaml
version: '1.0'
commands:
  diff:
    extends: [recommended]
    rules:
      operation:
        tags: none
```

The [diff configuration](/docs/reference/cli/diff#configuration) lists the node types and their
properties, and how to set different rules for requests and responses.

## Retire an operation on a schedule

Removing an operation is breaking, unless clients were told in advance. Deprecate it with a sunset
date first, and ship that: deprecating is not breaking.

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

Once the date has passed, the `recommended` rules treat the removal as non-breaking, and the same CI
job lets it through. `--today` sets the date of the comparison, to try it out before then.

For changes bigger than one operation, `apigenie evolve plan` does the planning. It takes the
document clients use and the one you want, and writes an expand wave (the new shape next to the old
one, deprecated, safe to ship now) and a contract wave (the target, to ship after the sunset):

```sh
apigenie evolve plan main.yaml openapi.yaml --out evolve-out/
```

`evolve` is [experimental](/docs/reference/cli/overview#experimental-commands); see
[evolve](/docs/reference/cli/evolve) for its recipes.

## Set the version and keep a changelog

`--update-version` writes the smallest `info.version` the changes allow, a new major version for
breaking changes, minor for non-breaking and patch for non-functional ones, and keeps the rest of the
file as it is:

```sh
apigenie diff main.yaml openapi.yaml --update-version
```

`apigenie changelog` compares the committed versions of a file and describes what changed for
clients, per commit or per release tag:

```sh
apigenie changelog openapi.yaml --revisions tags --tag-pattern 'v*' --unreleased
```

## Next steps

- [diff](/docs/reference/cli/diff): every option, the rules of each standard and the JSON report.
- [changelog](/docs/reference/cli/changelog): revisions, tags and output formats.
- [Publish API documentation](/docs/guides/publish-api-documentation): the changelog of each API on
  its documentation site.
