EventBridge Schema Evolution: Additive Changes Without Breaking Consumers

EventBridge Schema Evolution: Additive Changes Without Breaking Consumers
9 views
EventBridge makes publishing easy and breaking consumers even easier. A renamed field or tightened enum ships in minutes; twenty Lambdas fail overnight. Senior teams treat event schemas like public APIs: additive evolution, defaults on read, and contract tests that fail the producer’s PR before the bus sees the payload.
⚡ TL;DR: Register schemas; allow add-optional-field only by default. Consumers must ignore unknown fields and default missing optionals. Version major only on break; dual-publish during migration. Pair with DynamoDB Streams Outbox and CQRS Boundary Criteria.

EventBridge Schema Evolution: production guidance

json
{
 "$id": "order.placed.v1",
 "type": "object",
 "additionalProperties": true,
 "required": ["orderId", "customerId", "total", "currency"],
 "properties": {
 "orderId": { "type": "string", "format": "uuid" },
 "customerId": { "type": "string" },
 "total": { "type": "integer", "minimum": 0 },
 "currency": { "type": "string", "enum": ["USD", "EUR", "INR"] },
 "promoCode": { "type": "string" }
 }
}
python
# ci/schema_diff.py
BREAKING = {"remove_required", "rename", "narrow_type", "remove_enum_value"}

def classify(old: dict, new: dict) -> str:
 # illustrative — use json-schema-diff / buf breaking in real CI
 removed_req = set(old.get("required", [])) - set(new.get("required", []))
 if removed_req:
 return "breaking"
 # ✅ Adding optional properties is additive
 return "additive"
❌ Shipping currency from string to {code, precision} in place — that is a major version, not a “cleanup.”

Consumer defaults and unknown fields

typescript
// consumers/orderPlaced.ts
type OrderPlacedV1 = {
 orderId: string;
 customerId: string;
 total: number;
 currency: "USD" | "EUR" | "INR";
 promoCode?: string;
};

export function parseOrderPlaced(raw: unknown): OrderPlacedV1 {
 const e = raw as Record<string, unknown>;
 // ✅ Ignore unknown keys; default optionals
 return {
 orderId: String(e.orderId),
 customerId: String(e.customerId),
 total: Number(e.total),
 currency: e.currency as OrderPlacedV1["currency"],
 promoCode: e.promoCode ? String(e.promoCode) : undefined,
 };
}

Dual-publish for major migrations

Phase Producer Consumers
0 v1 only all on v1
1 v1 + v2 (same transaction / outbox) migrate one-by-one
2 v2 only when %v1 consumers = 0 v2
3 remove v1 schema from registry —
Wire dual-publish through the same outbox row with two detail-types (order.placed and order.placed.v2) so you never fork business transactions.

Contract tests on the producer PR

typescript
// __tests__/events.contract.test.ts
import { schemas } from "../schemas";
import { buildOrderPlaced } from "../producers/orders";

test("order.placed matches registry schema", () => {
 const evt = buildOrderPlaced(sampleOrder);
 expect(() => schemas["order.placed.v1"].validate(evt)).not.toThrow();
});

test("consumer fixture corpus still parses", () => {
 for (const fix of loadCorpus("order.placed")) {
 expect(() => parseOrderPlaced(fix)).not.toThrow();
 }
});

Closing checklist

✅ Dos – ✅ Put schemas in registry / repo with review – ✅ Allow only additive changes on minor versions – ✅ Consumers ignore unknowns and default optionals – ✅ Dual-publish across major migrations – ✅ Fail producer CI on breaking schema diffs ❌ Don’ts – ❌ Don’t rename fields in place – ❌ Don’t require new fields without a defaulting window – ❌ Don’t parse with as any and hope – ❌ Don’t delete detail-types while consumers remain – ❌ Don’t evolve enums by silently removing values

Related reading

Last updated on September 11, 2026 Related: Production-Grade Multi-Region E-Commerce Checkout on AWS with Aurora Global Database, EventBridge, and SQS; How to Build a Real-Time Fraud Detection and Decisioning Architecture on AWS with EventBridge, Step Functions, and DynamoDB; AWS Multi-Tenant SaaS Control Plane Architecture: API Gateway, ECS, DynamoDB, Step Functions, and EventBridge.

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.