Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Event Schema Evolution
HLD

Event Schema Evolution

Events outlive their producers' code — changing event formats without breaking consumers, forever.

The Permanence Problem

 events written TODAY get consumed for YEARS:

 [2024 topic data] ──► consumed by services deployed in 2026,
                       replayed in 2027, audited in 2030.

 you cannot UPDATE history. old payloads stay as-is.
 every consumer ever written must keep reading them.

 so the question isn't "what schema?" but
 "how does the schema CHANGE SAFELY, repeatedly, forever?"

The Compatibility Directions

 BACKWARD compatible: new consumers read OLD events.
   (required when topics retain/replay history — always!)
 FORWARD compatible:  old consumers read NEW events.
   (required during rolling deploys — always!)
 FULL: both. the production default for shared topics.

 concrete rules that preserve compatibility (avro/protobuf-style):

 SAFE ✓                              BREAKING ✗
 add field w/ DEFAULT                add required field
 remove field (was optional)         remove field w/ consumers of it
 widen numeric types (i32→i64)       narrow types (i64→i32)
 add union branch                    remove union branch
 rename ONLY via aliases             plain rename

 golden rule: ADDITIVE with defaults; deletions only after
 a full consumer-census confirms nobody reads the field.

The Rolling-Deploy Window

 during ANY deploy, old and new versions coexist LIVE:

 t0: all consumers v1        producer v1
 t1: some consumers v2   ←   producer STILL v1
 t2: producer v2         ←   consumers mixed v1+v2  ← danger zone!
 t3: all consumers v2

 forward-compat needed at t2: v1 consumers must not explode
 on v2 events. this is why "add optional field" is safe
 ("unknown fields ignored" semantics) and
 "change field meaning" never is.

Versioning Strategies

StrategyMechanismTradeoff
In-place evolutionadditive-only changescleanest; discipline required
Envelope version{v: 2, data: {…}}explicit; dual-code paths
New topic per majororders.v2 topicclean breaks; fan-out migration
Upcasting layertransform old→current at readhides versions from consumers
 pragmatic combo:
 minor changes: evolve in place (additive)
 breaking needs: new MAJOR type/topic + overlap window +
                 deprecation timeline for v1
 upcasting when history-replay demands uniform current shape
 (event-sourced systems especially).

Governance That Actually Works

 □ SCHEMA REGISTRY enforces compatibility at publish time
   (bad schemas rejected BEFORE they poison streams)
 □ CI contract tests: producers validated against registered
   schemas; consumers pinned to compatibility mode
 □ CONSUMER CENSUS before any removal: who reads field X?
   (registry usage tracking answers this)
 □ DEPRECATION windows communicated like API deprecations —
   because events ARE APIs with worse rollback stories.

Interview Framing

“We need to add tax_amount to OrderPlaced without downtime” scored walk-through: classify change (additive+default = backward&forward safe), rolling-deploy window explained, registry enforcement mentioned, and the removal-later path sketched (census → deprecate). Candidates who treat event schemas like internal function signatures fail this question everywhere it appears.

My Private Notes

Notes are auto-saved locally to this device.