Org Skills

Event versioning and contracts

Supporting material for messaging-patterns. Agents load it on demand; it ships inside the skill folder.

RawSource

Companion to messaging-patterns §4.

Compatibility matrix

Change to the payload Compatible within .vN? What to do
Add an optional field Yes Ship it; consumers must ignore unknown fields (System.Text.Json and Zod z.object do by default)
Add a value to an enum-like field Yes, if documented as extensible Consumers treat unknown values as "other"; never switch without a default
Make an optional field required No New version
Remove or rename a field No New version; keep the old name populated in .vN until retired
Change a type (stringnumber, date format) No New version
Change the meaning of a field (gross → net amount) No, even if the type is unchanged New version with a new field name
Change the partition key No New topic or new version; consumers assume ordering per key
Change the topic/queue name No New destination with dual-publish

Dual-publish recipe

  1. Publish orders.order-placed.v2 next to .v1 from the same outbox transaction (two rows).
  2. Migrate consumers one by one to .v2; each consumer's inbox key is (message_id, consumer), so both versions can coexist without double effects only if consumers subscribe to one version at a time.
  3. Watch consumption of .v1 (consumer group lag, subscription metrics) until it is zero for a full retention period.
  4. Stop producing .v1; delete the subscription after the retention window.

Header set

Header Purpose
message-id Dedupe key; equals the outbox row id
message-type orders.order-placed.v1
occurred-at RFC 3339 UTC, producer clock
aggregate-id / partition key Ordering key
correlation-id The business flow (order id, request id)
causation-id The message or request that caused this one
traceparent W3C trace context; propagated by OpenTelemetry instrumentation for most broker SDKs
tenant-id Never inferred from the payload on the consumer side
content-type application/json, application/avro, …

Schema publication options

Option Fits Cost
JSON Schema files in the owner's *.Contracts package (NuGet or npm) Modular monolith or few services in one org Consumers must update the package to see changes
Schema registry (Confluent, Apicurio, Azure Schema Registry) with Avro/Protobuf Kafka or Event Hubs, many consumers, compatibility rules enforced at publish time Operate the registry; producers fail fast on incompatible schemas
AsyncAPI document per service Documentation and generated consumer stubs Keep it in CI like OpenAPI (see api-design)

Consumer tolerance rules

  • Parse with a schema that ignores unknown fields and rejects wrong types (Zod: default z.object strips unknown keys; .NET: JsonSerializerOptions default ignores unmapped properties).
  • Treat a schema-invalid message as a permanent failure: dead-letter it with the validation error; do not retry.
  • Never depend on field order, on the absence of a field to mean "false", or on the producer's clock for business decisions.