Event-Driven Systems · Lesson 6

Event Schema Evolution and Compatibility Explained

Evolve event payloads safely with explicit semantics, compatibility checks, tolerant readers, staged rollout, replay evidence, and deprecation boundaries.

Event schemas live longer than many HTTP requests because records may be retained, replayed, copied into data platforms, and consumed by independently deployed applications. Safe evolution therefore requires both structural compatibility and stable business meaning.

Quick answer

Prefer additive optional fields with documented defaults, keep existing field meaning stable, and validate old producers against new consumers as well as new producers against supported old consumers. Use a new event type or major contract when meaning changes incompatibly. A schema registry check is necessary evidence, but it cannot prove semantic or replay compatibility by itself.

Prerequisites

Read Event Design with CloudEvents and AsyncAPI and API Versioning Explained. Know which envelope identifies the event and which schema describes its business payload.

Structural and semantic compatibility

Adding an optional promotionCode may be structurally backward compatible if old consumers ignore unknown fields. It is not semantically safe if a missing code used to mean “full price” but now means “look up a mutable promotion.” Renaming amountCents to amount while changing units is a new meaning, even if both fields are integers.

Write invariants in plain language beside the schema: currency uses ISO codes, minor units are integers, order version increases per order, and cancellation never means deletion. Tests can then assert meaning rather than only parser acceptance.

Compatibility directions

Backward compatibility asks whether a new consumer can read old data. Forward compatibility asks whether an old consumer can tolerate new data. Full compatibility combines both within a declared support window. Different schema technologies use those terms with specific rules, so record the exact tooling mode and version instead of saying merely “compatible.”

For JSON, decide whether unknown properties are allowed. For enums, an old generated client may fail on a new value even when the JSON shape is valid. For required fields, adding one breaks retained old events unless the reader supplies a safe default.

Tolerant readers with bounded meaning

A tolerant reader ignores data it does not need, but it must reject events that cannot preserve its invariant. Silently turning an unknown payment state into FAILED can trigger compensation. A safer consumer quarantines the unknown state, emits bounded diagnostics, and waits for an intentional compatibility decision.

Do not catch every deserialization error and commit the offset. That converts an observable incompatibility into lost work. Classify permanent invalid data, transient dependency failure, and unsupported-but-potentially-recoverable versions separately.

Stage producer and consumer rollout

For an additive field, deploy consumers that tolerate both shapes before producers start sending it. Observe adoption and errors, then make use of the field. For an incompatible semantic change, publish a new event type or channel, dual-read or translate during a bounded migration, verify results, and retire the old contract only after every supported consumer has moved.

Dual publication creates its own duplicate and divergence risks. Give the two representations a relationship that operators can trace, and do not let two consumers apply the same business effect independently during migration.

Replay is a separate contract

A new consumer may parse a five-year-old event but still call today’s external API or apply today’s tax rule, producing a different result. Decide whether replay rebuilds a projection, re-executes a business effect, or only audits history. Suppress irreversible side effects during projection rebuilds and record the code, schema, and reference-data versions used.

Failure scenario

The producer adds status: REFUNDED_PARTIALLY. An older consumer’s generated enum throws. Its non-blocking retry forwards the same permanent failure through several retry topics, losing per-partition ordering and delaying later records. Compatibility fixtures and an unknown-state quarantine policy would have contained the event without pretending the business state was understood.

Common mistakes

  • Equating parser success with unchanged business meaning.
  • Removing a field because no repository search finds a consumer.
  • Making a new field required before retained events and old producers can supply it.
  • Replaying events through live email or payment effects.
  • Keeping dual publication forever with no owner or retirement condition.

Production validation

Keep representative fixtures for every supported contract version. In CI, run new consumers over old fixtures and supported old consumers over new additive fixtures. Test unknown enum values, missing optional data, large payloads, invalid encodings, and duplicate events. During rollout, monitor deserialization failures, unsupported versions, quarantine age, consumer lag, and projection reconciliation—not only broker throughput.

Sources

Use the Event-Driven Systems Learning Path for the ordered course. Continue with Reliable Event Publishing, Dead Letter Queue Explained, and Event Ordering and State Convergence. Browse the Event-Driven topic cluster for every lesson.

Knowledge check

Check your understanding

Answer both questions correctly to mark this lesson as mastered. You can retry without penalty.

1. A producer changes amountCents to amount and silently changes units; why is parser acceptance insufficient?

2. What is the safe rollout order for an additive optional field?