Skip to content
← Insights

Data Contracts: How to Prevent Integrations from Breaking When Systems Change

A guide to defining verifiable data contracts, managing changes, and detecting incompatibilities before they affect integrations.

Data contract diagram between integrated systems

When a CRM feeds an ERP, a store sends orders to logistics, or an internal application publishes events for other systems, the integration depends on more than a URL, a queue, or a shared file. It depends on all parties interpreting data the same way over time. Data contracts for integrations turn that expectation into explicit, verifiable, and governable rules.

Their purpose is not to document an interface just once, but to prevent an apparently minor change—renaming a field, allowing a new status, or changing a monetary unit—from causing silent errors, reprocessing, or incorrect business decisions. They are especially useful when different teams, third-party applications, and deployments with independent schedules are involved.

What a data contract is and the problem it solves

What a data contract is and the problem it solves — Linkses visual guide

A data contract defines the operational agreement between the party that produces information and the one that consumes it. It applies to a service response, an event, a periodic file, a shared table, or a queue message. It describes what is delivered, what it means, under what conditions it is valid, and how it will evolve.

For example, an order message with orderId, status, and amount must specify whether the amount is in units or cents, which currency applies, which statuses are allowed, whether a cancelled order may have a zero amount, and whether the same message may arrive more than once. Without these rules, two systems may technically accept the same JSON and still behave incompatibly.

The contract reduces three common risks:

  • Direct breakages: a consumer cannot deserialize, validate, or process changed data.
  • Semantic errors: the format appears correct, but the meaning of a field or a business rule changes.
  • Invisible dependencies: a producer does not know that a field, value, or behavior is used by other consumers.

Documentation, schema, and operational contract

Documentation explains how an integration should work. A schema formalizes part of that explanation: structure, names, types, required fields, and, depending on the technology, constraints. Both are necessary, but they are not enough to operate an integration safely.

The operational contract combines the schema with decisions that can be reviewed and tested: owner, known consumers, compatibility policy, versioning, semantic rules, minimum quality, error handling, and change procedure. It can be represented with OpenAPI, AsyncAPI, JSON Schema, Avro, Protobuf, SQL, or another specification, but the tool does not replace those decisions.

A useful contract should include at least:

  • Identity and scope: name, purpose, channel, producer, owner, and sensitivity classification.
  • Structure: fields, types, cardinality, required status, formats, and valid examples.
  • Semantics: meaning, unit, time zone, currency, value catalog, and calculation rules.
  • Quality: uniqueness, ranges, relationships between fields, expected freshness, and ordering where relevant.
  • Operations: frequency, size limits, delivery order, retries, retention, and incident channel.
  • Evolution: version, compatible changes, changes requiring coordination, and retirement period.

Compatibility: assessing changes from an explicit perspective

Compatibility terminology may vary between teams. To avoid incorrect decisions, it is advisable to adopt the common schema-reading perspective used by many schema registries: backward compatibility means that the new schema or consumer can read data produced with the previous schema; forward compatibility means that the previous schema or consumer can read data produced with the new schema. Full compatibility requires both directions.

This definition does not make it possible to infer actual behavior simply by looking at an abstract rule. It must be verified with the format, serializer, reader schema, and the consumer's specific implementation. Adding an optional field with a default value, for example, may be backward compatible if the new version can fill in its absence when reading old data. But an older consumer will only be able to read the new message if its technology and implementation ignore unknown fields or if its reader schema correctly resolves that field through a default value. This should not be assumed without testing.

Before approving a change, ask specific questions: can the new version process messages already stored? Can the previous version continue processing messages emitted during a gradual deployment? What happens to external consumers that do not update at the same pace? The answer must come from representative tests, not only from the registry's compatibility label.

As a practical rule, lower-risk changes often include adding truly optional fields, expanding uninterpreted metadata, or correcting descriptions. Removing or renaming fields, changing types, narrowing ranges, altering units, reinterpreting nulls, or introducing enumeration values that trigger different logic usually require a new version and coordination. Temporarily retaining a deprecated field and publishing a retirement date provides a safer transition than removing it immediately.

Design according to the exchange type

The same principle takes different forms depending on the mechanism. In events, identify the fact that occurred, the business timestamp, the source, the event identifier, and the affected entity. State whether the event is immutable, whether it can arrive out of order, and whether correction events exist.

If delivery is at least once, the consumer needs a verifiable strategy to detect or tolerate duplicates. It may use an event identifier stored in a deduplication register, a business key with idempotent operations, a storage constraint, or an equivalent agreed mechanism. An idempotency key is a common option, but not a universal requirement. The important thing is to define which duplicates are expected, how long they are detected for, and what outcome their reprocessing must produce.

In synchronous service responses, the contract must distinguish business data, functional errors, and technical failures. It is preferable to define codes, error structure, fields that are safe for diagnostics, and retry conditions. For periodic loads, specify the time cutoff, time zone, whether the file contains a full or incremental load, the reconciliation key, and the behavior for repeated or missing records.

Contract testing before and after production

Contract testing automates verification that producer and consumer comply with the agreement. During development, validate valid and invalid examples against the schema and relevant semantic rules. Include edge cases: permitted nulls, unknown values, decimals, dates with a time zone, and combinations of dependent fields.

In continuous integration, the contract change must be compared with the latest published version. Controls can block incompatible modifications, require a new version, or require owner approval. It is also advisable to run reference consumers or tests agreed with critical consumers against test data produced by the new version.

Before production, test the coexistence between versions that will actually exist during deployment. After publishing, monitor failed validation rates, messages sent to quarantine, deserialization errors, processing lag, and the proportion of unknown values. Record technical identifiers, contract version, error type, and source; avoid including personal data, secrets, or full payloads when they are not essential for diagnosis.

Change governance, exceptions, and traceability

Change governance, exceptions, and traceability — Linkses visual guide

A lightweight and explicit workflow prevents the contract from becoming a forgotten document. The producer proposes the change with its rationale, expected impact, before-and-after example, compatibility classification, and retirement plan. Owners of affected consumers review the semantics and tests. A designated owner approves, publishes the version, and communicates the adoption window.

Exceptions must also be defined. If a value arrives out of range, rejecting the entire load is not always appropriate: it may be sent to quarantine, marked for review, or accepted with a quality flag, depending on business risk. For unknown catalog values, determine whether the consumer should ignore them, preserve them, apply safe behavior, or stop the process. Carefully distinguish between an absent field, a null field, and an empty value: they may express different situations.

Finally, maintain a versioned record of contracts, compatibility decisions, approvals, deprecation dates, and test evidence. This traceability makes it possible to answer who changed what, why it was considered safe, and which consumers needed to adapt. In this way, evolution stops depending on manual checks and becomes a verifiable technical discipline.

Linkses · Boost your business

Written and reviewed by the Linkses editorial team.