Setting up API compatibility checks in a CI/CD pipeline

Breaking API changes quietly make it into releases and break customer integrations. We set up compatibility checks directly in the build pipeline — turnkey, with rollback and a clear report. This reduces incident triage time and restores release predictability.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

API compatibility breaks silently: where the bottleneck is in CI/CD

A breaking contract change almost never looks breaking in the diff.

An engineer renamed a field, narrowed a type, added a required parameter, or removed a response branch — the reviewer sees a tidy edit of a few lines and approves it, because within this file everything is logical.

API compatibility checking doesn't kick in at this stage: we have neither a pinned contract nor an automatic comparison against the previous version, only the eyes of a person looking at the change in isolation from consumers.

Then the delayed effect kicks in. The consumer service breaks not at the moment of rollout, but on its next release or on the first call along a rare scenario — that is, hours or days after the cause has already shipped to prod and gotten lost among other changes.

Backward compatibility is violated, but there's no signal: the build is green, tests pass, metrics drop later and not where you'd look. Diagnosis turns into log analysis on the client side rather than in the CI/CD pipeline where the error was introduced.

The cost of such a failure is made up of three parts: the time spent finding the true cause among several releases, an emergency rollback or patch under pressure, and manual contract reconciliation between teams that has to be done after the fact.

The more services depend on a single API, the wider the blast radius: one missed breaking change drags a cascade of fixes across all consumers.

The root of the problem is that API compatibility checking sits in the wrong place — not where the decision is made. The contract lives separately from the code, version comparison isn't automated, and breaking changes aren't separated from safe ones at the level of build rules.

Let's break down how this gets closed in the pipeline — from pinning the contract to blocking an incompatible change before merge.

What API compatibility checking in the build pipeline provides

Contract checking in the pipeline is not "just another linter," but a way to catch incompatibility at the build stage, rather than at the moment a client gets an error in integration.

Automated contract checking compares the current branch's specification against the reference one and records discrepancies: removed fields, changed types, parameters that have become required, renamed response codes.

What exactly specification comparison catches

The failure class here is predictable: backward-incompatible changes that break already-connected consumers. Specification comparison provides an exact list — which method, which parameter, what exactly changed.

The engineer sees this in the build log before the artifact has gone to the registry. Build time increases by tens of seconds, not by hours of manual incident investigation.

Check modes and their cost

The mode determines what the pipeline does when a discrepancy is found. Warning is convenient during the rule's trial period: the team sees discrepancies, but the build proceeds.

Build blocking is the working mode for stable branches: an incompatible change simply does not pass through. Comparison with the stable branch cuts off accidental edits that break an already-released contract.

Option When it fits Limitations
Warning First weeks, trial of the rule Discrepancy is visible but does not block the release
Build blocking Stable branches, production contracts Requires explicit approval of changes
Comparison with the stable branch Multiple parallel releases Requires versioning discipline

OpenAPI Diff is one of the tools that provides a machine-readable report on discrepancies and integrates into the build step without manual configuration.

How we embed compatibility checking into the pipeline

API compatibility checking is not just about running a single utility: first you need to understand which contracts even exist and who their consumers are.

If you skip this, the rule will break the build on the very first harmless change, the team will start disabling it — and the protection will become a formality. That is why our order of work is fixed: from inventory to handing over the configured rules to your engineers.

  1. Contract inventory. We collect specifications (OpenAPI, gRPC schemas, AsyncAPI), match them against repositories, and find shadow contracts that exist only in code or in correspondence. The output is a registry of services with owners and consumers identified.
  2. Change classification. We divide edits into breaking and backward-compatible: removing a field, narrowing a type, changing requiredness, renaming a method. This is the foundation for the rules — without it, the check either misses breaks or blocks everything indiscriminately.
  3. Configuring rules and thresholds. We define what counts as a violation and what counts as a warning, set exceptions for deprecated fields and their lifetimes. We put the rules in the repository next to the code so they are versioned alongside it.
  4. Embedding into the pipeline. We compare the current specification with the baseline version from the main branch and return the result as a check status. On a breaking change, the build fails with the specific field and the consumer it will affect indicated.
  5. Test run on real changes. We run the rule on several past edits and on a deliberately introduced break — making sure there are no false positives and that the check catches what it was put in place for.
  6. Documentation and handover. We give the team a description of the rules, a bypass instruction for deliberate exceptions, and the procedure for updating the baseline specification.

After handover, the check lives on without our involvement: new services are connected using the described template, and disputed cases are resolved according to the fixed rules rather than in correspondence. Describe your task — we will get back to you with an estimate and a work plan.

How does build-time verification differ from environment verification?

Build-time verification and environment verification answer different questions. The build compares the contract of the new version against the already published one: method signatures, request and response schemas, error codes, field requiredness.

If a consumer service builds against a changed contract and breaks, that is visible before the artifact even reaches the registry. Environment verification catches something else: how the deployed service behaves under real load, with real data and neighboring dependencies.

Shifting left is about moving API compatibility verification to the earliest stage where it can be performed at all. At build time, only artifacts and the specification are available, but feedback comes within minutes and does not require standing up infrastructure.

Early detection of incompatibility costs one rewritten commit; the same error found in pre-production means rolling back a deployed environment, incident analysis, and re-running the entire release cycle.

The cost of fixing grows non-linearly as you move toward production. At build time, a contract fix is a change in one branch and a re-run. In the development environment, approvals with the consumer team and rebuilding dependent services are added.

In pre-production, data migrations are added on top of that if the contract has already leaked into storage, along with manual verification of scenarios that automation does not cover.

The choice depends on the release process. If services are released independently and the contract changes frequently, build-time verification is mandatory — otherwise incompatibility will surface at the consumer at the most inconvenient moment.

If releases are infrequent and monolithic, some checks can be kept in the development environment, but the basic contract comparison is still cheaper to do before deployment.

In practice, these two levels do not replace but complement each other: the build is responsible for contract compatibility, the environment for behavior under load. We break down how to set up both so that incompatibility is caught at the stage where fixing it costs the least.

Case study: how we removed breaking changes from releases

A product team from the fintech domain would approach a release confident that "the contract wasn't touched," and a day later would catch a wave of incidents at consumer services: one had its response parsing break, another had authorization fail because of a changed field.

Feedback came from consumers, not from the pipeline, so rolling back a release was launched blindly and took hours.

We started with an inventory: we collected all public contracts and recorded reference schemas and endpoint descriptions in the repository alongside the code.

Then we built API compatibility checking directly into the pipeline — at the build stage, every commit is compared against the reference, and any field removal, type narrowing, or change in requiredness is flagged as a breaking change. It doesn't move forward without explicit confirmation and a new endpoint version.

What changed in numbers

  • Share of releases with breaking changes: from roughly every fourth to zero — disputed edits are cut off before the merge, not after the rollout.
  • Number of incidents at consumer services: dropped severalfold, because incompatibility is visible at the build stage, not in production.
  • Release rollback time: cut from hours to minutes — rollback became a planned operation based on a known previous contract version, rather than an emergency investigation.
  • Contract stability no longer depends on an individual developer's discipline: the checking rule is the same for all branches.

We also set up a discrepancy report: it shows exactly which consumer is affected and which field changed. This eliminated the "everything broke on my end" back-and-forth — the culprit and the scope of the edit are immediately visible at the input.

We built the check on the basis of OpenAPI and comparison of specification versions in the pipeline.

If your releases have already led to breakages at adjacent teams — describe the task, and we'll come back with an estimate and a plan for implementing compatibility checking.

Doubts before adoption: access, migration, rollback

Compatibility checking is built into the existing pipeline rather than replacing it: the rule runs at the build stage, before the artifact is published, and compares the contract of the new revision with what has already been delivered to consumers.

Repository access is needed only to read the specification and write the check status — the rule does not receive rights to modify someone else's code or to access production environments.

If the pipeline already builds and publishes artifacts, no separate infrastructure is deployed for the check.

What if the rule blocks a release on the eve of rollout?

The rule distinguishes between an incompatible change and a warning. Breaking edits — removing a field, narrowing a type, changing requiredness — block the build, because they are guaranteed to break consumers. Adding an optional field or a new method passes with a note.

During the migration period, the rule is switched to observation mode: discrepancies are written to a report, but the build is not stopped — this way the team sees the debt without losing release velocity.

Is migrating the rules a separate effort? No. Existing specifications are connected as-is; we describe only those exceptions where the contract historically diverges from the code, and record them in an explicit list. From there, the list only shrinks.

How are changes rolled back? The rule lives in the repository as an ordinary configuration file. A rollback is a revert of the commit, which restores the previous check behavior in a single pipeline run, without rebuilding environments.

What if consumers are already broken? The report shows exactly which fields and methods are affected and which consumers use them — edits are made in a targeted way, not "by eye."

After delivery, the customer receives:

  • the compatibility rule configuration, versioned together with the code;
  • the check policy: which changes block the build and which pass with a warning;
  • a rollback plan with a step-by-step description of returning to the previous behavior;
  • a list of exceptions and historical contract divergences;
  • a discrepancy report template with mapping to consumers;
  • instructions for switching the rule to observation mode during migration;
  • a description of the access and permission boundaries that the rule requests.

Describe your task — we will come back with an estimate and an implementation plan for your pipeline.

Timeline for implementing compatibility checks by stage

Implementing compatibility checks happens in waves: first we lock down the contract and connect it to the build in observation mode, then we switch discrepancies to blocking mode.

This order gives the team time to get used to the signals and doesn't halt releases in the very first week. Below is the approximate duration of the stages; it depends on the number of services, the release frequency, and how well the current contract is described.

Stage What happens Approximate timeline
Inventory We compile a list of services, versions, and consumers, and determine where the contract is actually locked down 3–5 days
Contract lock-down We describe the current state as the reference, and agree on versioning rules 1–2 weeks
Build integration The check runs on every change in observation mode, discrepancies are only logged 1 week
Noise triage We filter out false positives, refine the rules, and clean up accumulated discrepancies 1–2 weeks
Blocking mode An incompatible change halts the build until explicit confirmation 2–3 days
Process and handover We describe the contract update procedure, train the team, and assign owners 3–5 days

The order of the stages matters more than their duration: if you skip noise triage and enable blocking right away, builds will start failing on accumulated discrepancies, and trust in the check will disappear within the first few days.

The team's workload is distributed unevenly — inventory and contract lock-down require developer involvement, after which the work is carried out by support engineers.

Can it be sped up? Yes, if the contract is already described in a machine-readable way — the lock-down and noise triage stages are cut in half. What if there are many services?

The stages run in parallel across groups of services, and the overall timeline grows not linearly, but along the longest dependency chain.

Describe your task — we'll come back with an estimate and a plan tailored to your environment.