A data contract records what consumers can rely on and how producers will handle changes.
Changing from gross to net amounts
Agreement amendment · DC-12
Payload fragment · unchanged shape
{
"amount": 1200,
"currency": "GBP"
}
Existing agreement
Gross amount
Producer proposal
Net amount
Consumer review required
The numeric type stays valid when the meaning changes. Agree the tax treatment and calculation before consumers adopt the new definition.
Begin with the consumer’s dependency
A data contract records the agreement between a producer and its consumers. Begin with the decisions that depend on the data, because a list of field types cannot explain the consequence of a change. An inventory consumer may need available quantity at a particular location and effective time. A reporting consumer may instead need a stable period close snapshot with reproducible historical values.
Identify the producer owner, supported consumers and escalation route. State the entity grain, intended use and boundaries of the promise. A contract for an order header should not imply completeness of order lines unless that is explicit. Documenting the dependency first helps teams distinguish essential compatibility from convenient assumptions that a consumer has made without agreement.
Use schemas for structural guarantees
JSON Schema can describe the structure of JSON documents, including properties, required fields and composition. OpenAPI and AsyncAPI provide broader ways to describe request or message interfaces. Choose a specification appropriate to the delivery mechanism, and keep examples consistent with it. A schema becomes more useful when validators run in development and at a controlled ingestion boundary.
Be deliberate about unknown fields, null values and empty collections. An absent field may mean unavailable, inapplicable or deliberately withheld; an empty array may mean there are no items or that items have not arrived. Specify those meanings separately from type declarations. Include invalid examples so consumers can see where validation ends and operational exception handling begins.
Write down the business semantics
Structural compatibility does not guarantee semantic compatibility. Changing a monetary field from gross to net while keeping the same numeric type can silently alter every consumer’s result. State units, currency treatment, rounding rules, timezone assumptions and the meaning of status values. Identify whether timestamps represent a business occurrence, source persistence or delivery to the consumer.
Record source authority and identity rules. If a customer identifier is unique only within one region, the contract must not present it as globally unique. If a quantity can later be corrected, explain how a consumer recognises the correction. Small semantic decisions deserve explicit examples because different teams can reasonably interpret the same field name in incompatible ways.
Who relies on the old meaning?
Invoice totals report
The report expects gross amounts. Interpreting a net amount as gross could change its result while the field still satisfies a numeric type check.
Invoice source owner
Define the proposed meaning, currency treatment, adjustments and rounding.
Report owner
Identify affected calculations and test existing readers with new records, and new readers with retained records.
Joint review
Agree a migration and deprecation plan before releasing the changed promise.
The amount remains numeric while its proposed business meaning changes.
Define compatibility in both directions
A producer change may be readable by an older consumer while an older record remains unreadable by a newer consumer. Review both situations, especially when retained events can be replayed. Adding an optional field is often easier to accommodate than changing an existing field’s meaning, but actual compatibility still depends on consumer behavior and schema validation settings.
Separate the version of the interface contract from the version of a particular entity record. Publish a migration path for breaking changes and define a deprecation period based on known consumers. Test representative old payloads against new readers and new payloads against supported readers. Compatibility should be demonstrated with evidence rather than inferred solely from a version number.
Include delivery and quality expectations
A valid payload can arrive too late to be useful. State freshness expectations, ordering scope, duplicate behavior and the conditions under which records may be missing. Explain retry and replay semantics, along with the ownership of rejected records. An integration that drops invalid data without a visible exception process may satisfy a schema while violating the business agreement.
Define observable quality indicators for the important promises. Examples include the age of the latest source observation, the share of records missing a required business identifier and reconciliation differences between producer and consumer totals. Agree how violations are communicated and who decides whether a downstream workflow may continue. Avoid using a single generic quality score to hide distinct failure modes.
Make the contract part of delivery
Store contracts with a review history and connect changes to the engineering workflow. Validate examples, run compatibility checks and notify affected owners before release. Keep a consumer inventory current enough to identify who depends on a field or event type.
Review contract incidents after recovery. Determine whether the promise was wrong, the implementation violated it or a consumer relied on an undocumented assumption. Update the relevant test and ownership record. Give affected consumers a migration path and enough time to adapt before incompatible changes reach production.