Skip to content

Breaking Changes

Nicolas Pepin-Perreault edited this page Mar 21, 2022 · 4 revisions

The following sections describe how backwards compatibility is maintained in our Java and Go public API, to what extent it is maintained and what you must do if you want to break backwards compatibility.

Java

Currently, there are four modules that comprise our public API in Java: zeebe-client-java, zeebe-test, zeebe-protocol, zeebe-exporter-api, and zeebe-bpmn-model. Breaking changes in any of these modules will be detected by the Revapi plugin and fail the build. If these changes are intentional, then you can make revapi ignore them by adding the desired ignored change in the specific module's ignored-changes.json file. You can find this file in any of these module's root directory - if it does not exist it, simply create it yourself. The following is an example of what it can look like:

[
  {
    "extension": "revapi.differences",
    "id": "differences",
    "configuration": {
      "differences": [
        {
          "justification": "We added a new value, DEAD, which changed the ordinals; however, SBE enums do not/should not rely on ordinals, but rather on the defined value.",
          "code": "java.field.enumConstantOrderChanged",
          "classQualifiedName": "io.camunda.zeebe.protocol.record.PartitionHealthStatus",
          "fieldName": "NULL_VAL",
          "oldOrdinal": "3",
          "newOrdinal": "4"
        }
      ]
    }
  }
]

This would ignore the addition of a new enum field in a generated enum. Please make sure when ignoring changes to always provide a justification.

At each release, the ignored-changes.json files will be deleted and the version against which revapi compares the current state will be updated to the release version. If, for some reason, you wish to change the name of the ignored changes JSON file, you must change the ignored.changes.file property in parent/pom.xml but not in the release scripts.

Failure handling

Revapi performs semantic analysis of the archives under test and compares them with a specific previous version. It will detect not only semantically breaking changes, but also unexpected changes which may lead to breaking usages, such as leaking internal/private types via your public APIs. Normally, when an error occurs, it will produce the JSON you need should you wish to ignore this change. You can then add this to the ignored change's differences extension. Note that it only produces the differences themselves, not the extension wrapper, so you will need to wrap it yourself if the file does not exist yet.

For example, on failure, it may output:

        {
          "code": "java.field.enumConstantOrderChanged",
          "classQualifiedName": "io.camunda.zeebe.protocol.record.PartitionHealthStatus",
          "fieldName": "NULL_VAL",
          "oldOrdinal": "3",
          "newOrdinal": "4"
        },

You cannot simply create a JSON file with only this content. You will need to wrap as shown in the example above.

Writing arbitrary rules

You can find an explanation of the differences extension of Revapi here, and of the filter extension here. These are the extensions you will most likely use.

You can find here a list of all the possible differences, here a longer explanation on how to do semantic matching, and finally here documentation on their DSL to do structural search, i.e. what they call classif.

Go

In the Go client, every exported symbol in a non-internal package is a part of our public API. It's worth mentioning that Zeebe does not forbid every type of breaking change in its Go code. The reasoning for this can be found here but for the purposes of this wiki suffice it to say that we try to provide the Go1 compatibility guarantee. Notably, this allows adding fields to structs and method to types.

Breaking changes are detected with the gocompat tool which also allows you to easily ignore changes that fall into the Go1 compatibility guarantee. To use it, you can run the following command in the clients/go directory:

gocompat compare --go1compat ./...

The previous command will compare the current set of exported symbols against the .gocompat.json file present in clients/go. This file is re-generated during every release and whenever someone wants to merge backwards-incompatible API changes. You can regenerate it by running the following command in the clients/go directory:

gocompat save ./...