Representative interview topic

Data Engineering Interview: How Do You Design and Enforce a Data Contract?

DataHard
Offer.cc Editorial TeamPublished Updated

Question

A company has 15 producers publishing order events to 40 consumers. How would you design and enforce a data contract so schema changes, semantic drift, freshness breaches, and bad records are caught before they silently corrupt downstream data?

Prompt and scope

Treat this as a data-engineering design question. Assume 2,000 order events per second at peak, a 15-minute freshness target, and a 99.5% completeness target for required fields. The contract must cover structure, meaning, quality, service levels, ownership, privacy tags, and the process for changing any promise.

The useful boundary is the data product between an upstream producer and downstream consumers. A database table definition alone is too narrow: it cannot state whether total_amount includes tax, who owns the feed, or what happens when freshness is missed.

What the interviewer is testing

The interviewer wants to hear a contract that is executable, versioned, and owned. A weak answer lists Avro or JSON Schema. A strong answer separates compatibility from semantic correctness, places checks at the producer boundary, and gives consumers a predictable breach and migration path.

You should connect every field to a consumer decision: reject, quarantine, transform, alert, or continue with an explicit degraded state. You should also explain how the contract avoids becoming an undocumented approval queue.

Clarifications that change the design

  • Is the source an append-only event stream, a mutable table, or both? Mutable snapshots need keys, update semantics, and deletion rules.
  • Which guarantees are hard launch gates? A payment event may reject on an invalid currency, while an optional marketing label may be quarantined.
  • Can consumers lag one version? If yes, publish a compatibility window and transformation; if no, require a coordinated cutover.
  • Are events replayable and do they contain personal data? Replayability affects retention, and privacy tags affect masking, access, and deletion handling.

A 30-second answer framework

“I would start with the consumer use cases and write a versioned, machine-readable contract. It would define schema and semantics, required-field and domain checks, freshness and completeness SLOs, ownership, privacy tags, and an evolution policy. Producers validate before publishing; the registry checks compatibility in CI; runtime checks quarantine bad records and expose metrics. A new version is additive by default, with a dual-publish or transformation window for breaking semantic changes. Every breach has an owner, replay path, and consumer-visible status.”

Step-by-step design

1. Define the contract object

Give the dataset an identifier and version. For each field record type, nullability, unit, business meaning, allowed values, sensitivity, and whether unknown fields are allowed. For the order event, state whether occurred_at is event time, whether amounts are integer minor units, and whether a cancelled order remains visible.

Add ownership, support contact, retention, freshness, delivery frequency, and availability. A service level is measurable: freshness can be the age of the newest accepted record, while completeness can be the ratio of non-null required fields over a window.

2. Separate checks by failure mode

Schema checks catch missing fields and incompatible types. Domain checks catch an amount below zero or an unsupported currency. Relational checks catch duplicate event IDs or an order transition that skips a required state. Freshness and volume checks catch a stalled producer or a partial partition.

Keep the test result with contract version, producer build, partition, sample window, and failed rule. That evidence lets a consumer decide whether to pause, backfill, or accept a bounded degradation.

3. Enforce before and after publication

In CI, compare the proposed schema with the registered version and run representative contract tests. At runtime, validate at the producer boundary before an event enters the shared stream. Send invalid records to a quarantine stream with the original payload, rule failures, contract version, and a replay key.

Consumers should still validate critical invariants at their boundary. Producer enforcement prevents many incidents; consumer checks protect against misconfigured routes, old producers, and transformation bugs.

4. Make evolution explicit

Treat adding an optional field as a compatibility-preserving change only when old consumers tolerate unknown fields. A rename is breaking because the meaning or field name changes. Prefer add-and-deprecate: publish the new field, dual-write or transform, migrate consumers, measure reads of the old field, then retire it after a stated window.

For a semantic change such as switching total_amount from tax-inclusive to tax-exclusive, a new version and new field are safer than reusing the name. If a consumer must remain on the old view, use a versioned transformation and label the converted value.

5. Define breach handling

Use severity tiers. A malformed payment event is rejected and quarantined. A freshness breach pages the owner and marks the data product stale. A noncritical description failure can continue with a metric. The contract should state who can override a gate, for how long, and which evidence is required.

Do not silently drop records. Track accepted, rejected, quarantined, replayed, and duplicate counts by producer and contract version. A replay must be idempotent, so the sink uses the event ID and contract version to avoid creating a second business effect.

6. Verify the operating model

Run contract tests on fixture data, compatibility tests on every proposed version, and canary validation on a sampled production partition. Test late events, duplicate IDs, unknown enum values, null required fields, timezone mistakes, and a producer that stops sending.

The useful dashboard combines breach rate, freshness age, completeness, consumer lag, quarantine depth, replay success, and time to owner acknowledgement. A green schema check with a stale feed is still a failed data product.

High-quality sample answer

“I would treat the order stream as a versioned data product. I would first list the consumers and define the event semantics: event time, amount units, currency, identity, and state transitions. The contract would then carry schema, domain and relational rules, freshness and completeness SLOs, ownership, retention, and privacy tags.

“The registry would reject incompatible changes in CI. Producers would validate before publishing, while a runtime validator sends bad records to quarantine with the failed rule and contract version. Consumers keep a small set of critical checks because routing or transformation can still be wrong.

“I would make evolution additive by default. For a rename or semantic change, I would add a new field or version, dual-publish, migrate consumers, measure old-field reads, and retire the old version only after the compatibility window. Freshness and quality breaches have explicit severity, owners, alerts, and replay procedures. I would verify the design with fixtures, canaries, late and duplicate events, and metrics for freshness, completeness, quarantine depth, and replay correctness.”

Common mistakes

  • Mistake → Failure → Fix: Calling a schema the whole contract → semantic changes and ownership remain implicit → document meaning, SLOs, owners, and change policy.
  • Mistake → Failure → Fix: Rejecting every invalid record synchronously → one bad event can block a whole partition → quarantine with bounded backpressure and a replay key.
  • Mistake → Failure → Fix: Claiming additive fields are always safe → strict consumers may fail on unknown fields → verify consumer compatibility before allowing the change.
  • Mistake → Failure → Fix: Alerting only on schema mismatch → stale or incomplete data can still pass schema checks → monitor freshness, volume, completeness, and business invariants.
  • Mistake → Failure → Fix: Reusing a field name after changing its meaning → historical and new values become incomparable → create a new version or explicitly transformed field.

Follow-up questions and responses

What if producers cannot all upgrade together?

Keep the old contract active, add the new field or version, and accept both during a measured window. A compatibility adapter can translate old input, but it must expose conversion loss and a retirement date rather than hiding the difference.

What if the contract test passes but the metric is still wrong?

That is semantic drift. Add a business-level invariant or reconciliation check, compare against an independent source, and record the disputed definition in the contract. Structural compatibility cannot prove that a producer applied the right business rule.

How do you prevent quarantine from becoming a data graveyard?

Give each rule an owner and an expiry target, retain the original payload and contract version, and measure queue age and replay success. A daily review should classify failures as producer bugs, contract defects, or expected exceptions.

When would you avoid a data contract?

For a private, short-lived table with one owner and no downstream promise, a lightweight schema and tests may be cheaper. Introduce the fuller contract when multiple teams, replay, regulated fields, or freshness commitments make implicit assumptions risky.

Public sources

Related questions