Data Contracts: Decisions That Matter Before the First Build

Krishnam Murarka explains data contracts with practical context for founders: architecture, risks, implementation choices and operating signals.

Krishnam Murarka Updated 2026-07-14 Data & Analytics

Data Contracts Decisions That Matter before the First Build

A data contract earns its keep at the moment a producer changes something that another team cannot see. Imagine an order service replacing a numeric discount with a formatted string. The event still arrives, the pipeline still completes, and a revenue report still renders; only the meaning has quietly broken. The expensive part is not the failed parse. It is discovering the failure after a pricing decision, customer message, or finance close has relied on it. A useful contract names the exchange, states what consumers may rely on, and gives both sides a controlled way to change the promise. It combines structure with meaning, timing, ownership, compatibility, and a response when the promise is not met. This guide focuses on those decisions before a team buys a registry, rewrites a pipeline, or publishes a catalog.

Begin with the exchange that can hurt the business

Choose one producer-consumer boundary where a wrong value has a visible consequence. It might be shipment status feeding customer notifications, subscription state feeding entitlement checks, or account currency feeding a financial model. Write the exchange in plain language: who publishes which fact, who consumes it, how quickly it must arrive, and what decision depends on it. Then collect a handful of real payloads, including a late record, an empty optional field, and the largest legitimate value. Those examples are more useful than an abstract schema because they expose hidden assumptions about time zones, identifiers, units, defaults, and privacy. A contract owner must be able to approve a change and decide whether an exception is safe to publish, safe only with a warning, or unsafe to release.

Contract cardExample for an order eventWhy it matters
PublisherCheckout service, owned by commerce engineeringA named source can explain and authorize a change.
ConsumerFulfillment allocation and delivery messagingImpact is assessed against real downstream behavior.
Meaningorder_id is stable; status is the latest accepted fulfillment stateA valid-looking value cannot silently acquire a new definition.
Service promiseAvailable within 10 minutes; late records are marked provisionalConsumers know when to wait, warn, or stop.
Change routeVersion, notice, compatibility check, migration ownerThe next release has a visible path instead of a surprise.

Keep shape and meaning in separate review lanes

A schema can tell a validator that status is a string and amount is a number. It cannot, by itself, tell a consumer whether amount is gross or net, whether the timestamp means occurrence or ingestion, or whether a missing value means unknown, not applicable, or not yet received. Review those questions separately from field shape. The JSON Schema Core Specification is useful for structural assertions and references; the Apache Avro specification is a useful anchor when named types and reader-writer evolution matter. Neither document supplies the business definition for your order status. Put the definition, examples, units, exclusions, and privacy classification beside the machine-readable schema.

Use examples to pin down semantic edges. If a customer can have several addresses, say whether the contract publishes the billing address, shipping address, or a selected default. If an event is retried, say whether event_id identifies the attempt or the business action. If a field is redacted, identify the redaction state rather than presenting an empty string. A domain reviewer should approve these examples because they are where analytical definitions, product behavior, and legal obligations meet. This separation also makes disagreements productive: engineers can discuss validation mechanics while the business owner resolves what the value is supposed to mean.

Choose compatibility rules like a migration policy

Six-stage data contracts change path from exchange framing through compatibility, migration, and review.
A six-stage contract change path connects semantic agreement to migration evidence and safe replay.

Compatibility is a business decision expressed through technical rules. Adding an optional field may be backward-compatible for a tolerant reader, while changing an identifier from account scope to user scope may be structurally valid and operationally breaking. Decide whether old consumers must read new data, whether new consumers must read old data, and how long both versions can coexist. A breaking change needs a migration window, a named consumer owner, a deprecation date, and a rollback or dual-publish plan. The OpenAPI Specification offers a helpful model for making interfaces, parameters, payloads, and responses explicit, even when the data exchange is not an HTTP API. Record compatibility decisions in version control so the rule travels with the contract.

Proposed changeContract classificationConsumer actionRelease evidence
Add nullable fieldUsually additiveIgnore until adopted; document null semanticsOld-reader test and example payload
Rename fieldBreaking unless aliasedMigrate query, model, and alert referencesConsumer inventory and dual-read result
Add enum valuePotentially breakingHandle unknown value explicitlyFixture for the new value and fallback state
Change business meaningBreaking even if shape is unchangedApprove definition, backfill or split historyDomain sign-off and historical comparison
Remove fieldBreakingProve no active consumer remainsSearch results, owner confirmation, retention date

Find every consumer before the producer changes

The visible dashboard is rarely the whole dependency. Search repositories, transformation manifests, scheduled queries, notebooks, exports, alerts, reverse-ETL jobs, and operational scripts. Ask each consumer owner what it assumes about nulls, ordering, duplicate delivery, late arrival, and historical corrections. A registry can improve discovery, but a registry entry is not proof that the inventory is complete. For a small team, a reviewed consumer map in the same change request may be enough. Give each dependency a disposition: migrate before release, tolerate the new version, remain on a compatibility view, or retire. This turns a vague notification into a set of owned actions and exposes the consumers that would otherwise surface only after deployment.

  • Search code, models, exports, alerts, and operational scripts for active consumers.
  • Ask each consumer owner what it assumes about nulls, ordering, duplicates, and late data.
  • Assign every dependency a migration, compatibility, or retirement disposition.
  • Record the release window, consumer owner, and evidence required for each action.
  • Keep the map with the contract change so it can be reviewed when the promise moves.

Put contract checks where a release can still stop

Validate at multiple points because each check catches a different class of failure. The producer test verifies that emitted records match the declared shape and required fields. A consumer test verifies that a real model or application can interpret the payload. A semantic fixture verifies units, status transitions, time behavior, and duplicate handling. A delivery check verifies that the proposed version and its compatibility policy are present before publication. The dbt model contracts feature can make selected model columns and types enforceable; the dbt model contracts documentation is a concrete reference for that pattern. Keep a small set of named fixtures under version control. A hundred generic tests are less persuasive than five examples that represent the decisions the exchange supports.

Do not make every violation a silent coercion. Classify the record or batch as accepted, quarantined, provisional, blocked, or corrected, and expose that state to consumers. Quarantine should preserve the original payload, contract version, failure reason, first-seen time, and owner; otherwise the team cannot investigate or replay it. A blocked release should identify the incompatible consumer or missing approval rather than leaving a generic red build. A provisional result may be appropriate for late data if readers can see its cutoff and correction behavior. These states keep imperfect data visible without forcing every downstream user to reconstruct what the pipeline decided.

Recovery is part of the contract. Decide whether the producer can replay the original event, whether a consumer can reprocess it idempotently, and whether a correction needs a new version or an amended record. Set a maximum quarantine age and an escalation path for records that affect customer access, money, or regulatory reporting. A repair that changes history should leave an audit trail linking the original value, corrected value, reason, approver, and effective time. The purpose is not to promise perfect data. It is to prevent an ordinary contract violation from becoming an invisible divergence between systems.

Provenance makes the response explainable. Retain the producer version, contract version, source identifiers, processing run, event time, ingestion time, and correction history for important outputs. The W3C PROV Data Model gives teams a useful vocabulary for connecting entities, activities, and agents; the W3C Data Quality Vocabulary helps describe measurements and the context in which quality was assessed. Use those ideas proportionately. A small team may need only a versioned changelog, a run identifier, and links to the owner. The test is whether someone investigating a disputed value can reconstruct what was published, from which input, under which rule, and who accepted the exception.

Pilot one boundary and rehearse the awkward path

Start with a boundary where both sides can meet quickly and where a wrong interpretation is costly enough to matter. Shadow the current exchange for a few days so the team can compare the declared promise with actual payloads. Then introduce the contract in stages: validate without blocking, notify on violations, quarantine a deliberately malformed fixture, and finally enforce the release gate. Ask the consumer owner to perform a migration, not just approve a document. Have the producer send a late record, an unknown enum value, a duplicate, and a semantically changed value. The rehearsal should end with a verified correction or replay and a short record of what the team would do at 2 a.m.

  • Select one consequential exchange and name its business and technical owners.
  • Capture real payloads, edge cases, timing expectations, and privacy constraints.
  • Publish structure, semantics, compatibility policy, examples, and change contacts together.
  • Test producer output, consumer interpretation, and release behavior with named fixtures.
  • Rehearse quarantine, replay, rollback, and communication before enforcing the gate.

Measure migration safety, not document volume

A contract program can look busy while consumers remain surprised by changes. Track the signals that reveal whether the boundary is becoming safer: breaking changes caught before publication, time from notice to consumer migration, violations quarantined with an owner, replay success rate, unresolved exceptions by age, and the number of semantic disputes discovered after release. Pair those counts with a few outcome checks, such as fewer manual corrections in fulfillment or fewer finance reconciliations caused by incompatible payloads. Do not turn the measures into a universal score. Their value is diagnostic: a rising quarantine queue may indicate a weak producer, an unclear definition, or a consumer that never received the migration window.

Key takeaways for data contracts

  • A contract is an operating promise between a named publisher and named consumers, not a schema file by itself.
  • Separate field shape from business meaning, time behavior, units, privacy, and examples.
  • Classify changes by consumer impact; a semantic change can break trust without changing a single data type.
  • Make violations visible and recoverable through explicit states, preserved payloads, ownership, and replay evidence.
  • Judge the practice by safer migrations and fewer downstream surprises, not by the number of catalog entries.

Frequently asked questions about data contracts

Do we need a registry or new platform first? No. Begin with one high-consequence exchange, a versioned contract, a consumer map, and checks that can run in the existing delivery path. Add a registry when discovery and ownership are becoming the bottleneck. Is a schema enough? No. The contract also needs definitions, timing, compatibility, examples, and a response to violations. Who owns the decision? The producer owns what it publishes, a business owner approves meaning and material exceptions, and each consumer owns its migration. Should every bad record block publication? Not always. Block when the decision could be materially wrong; quarantine or mark provisional when the business has an agreed, visible way to continue. When is the contract ready? When both sides can demonstrate normal processing, a breaking change, an incomplete record, and a correction without relying on private knowledge.

Conclusion

The first useful data contract is modest: one exchange, one decision, one compatibility policy, and one practiced recovery route. It should let a producer change with confidence because consumers can see what changed, why it matters, and what action is theirs. Read the adjacent data quality decisions guide, ELT workflows decisions guide, and data lineage decisions guide for related operating concerns. The official specifications linked above can sharpen implementation choices, but the contract becomes real only when owners use it to approve, block, migrate, quarantine, and recover from an actual change.

A strong contract does not make change impossible. It makes the cost of change legible before release: which consumers move, which records need replay, which historical values need explanation, and which owner can accept residual risk. That is the difference between a document that describes a pipeline and an agreement that protects a decision.

Continue with related articles