Capturing Truth — and the Schema-Evolution Myth
How truth gets into a system in the first place, and why doing it wrong shows up as schema churn
Truth doesn’t change. But a system does not contain the truth — it contains only the truth it managed to capture. Before any of immutability’s benefits can follow, there is a prior and entirely practical question that the previous article left open: how does truth get into the system in the first place, and how do we capture it without quietly distorting it?
Everything hinges on a single choice made the moment an event is defined. There are two ways to put truth into a system. You can capture what actually happened — record the irreducible fact at the grain it occurred. Or you can design an event — build a convenient structure that serves whoever is going to consume it. These sound similar. They produce systems that behave in opposite ways the instant requirements change, and the difference between a record that stays true and one that churns endlessly comes down entirely to which of the two you did.
The cleanest place to see the choice — and the cost of getting it wrong — is a phenomenon engineers treat as an inevitable fact of life: schema evolution. Change a field name, add a required property, remove something that downstream consumers depend on, and suddenly you have a coordination problem spanning multiple teams, multiple services, and potentially years of stored events.
Entire tools exist to manage this. Schema registries. Compatibility rules — backward, forward, full. Versioned event types. Migration frameworks. The complexity is real, and the tooling that has grown up around it is sophisticated and carefully designed.
But there is a question that rarely gets asked: why does the schema need to change in the first place?
The answer, when examined honestly, is almost always the same. The schema needs to change because the event was designed — built to serve the needs of its consumers — rather than captured from reality. Schema evolution is not inherent to event-driven architecture. It is a symptom of the very choice this article is about: designing events instead of capturing truth. So before the symptom, the choice itself.
Two ways to define an event
There are two fundamentally different ways to define an event.
The first way is to design it. You look at what downstream consumers need, gather their requirements, and produce an event schema that satisfies them — often a bundled event in disguise. An OrderPlaced event might contain the customer’s shipping address, the items with their prices and descriptions, applied discount codes, the payment method, and a dozen other fields that various consumers find useful.
The second way is to capture it. You ask what actually happened, at the finest grain at which it happened, in the language of the person or process that made it happen. An OrderPlaced event in this model contains almost nothing: an order identifier, a customer identifier, a timestamp. Because those are the irreducible facts of the act of placing an order. Everything else is context that existed before the order was placed, or consequence that followed from it.
The designed event feels richer. The captured event feels sparse. But they have radically different properties when requirements change.
Why designed events need to evolve
A designed event is a snapshot of your current understanding of what consumers need. That understanding changes constantly.
A new consumer needs a field that the current schema doesn’t include. A regulatory requirement introduces new mandatory data. A fraud detection system needs something that was never considered. An existing consumer changes how it uses a field.
Each of these is a reason to change the schema. And changing the schema creates a coordination problem: existing stored events don’t have the new fields, old consumers don’t understand new fields, the system has to handle multiple versions of the same event type simultaneously.
This is the schema evolution problem. It is real, it is painful, and it is the direct consequence of designing events around consumer needs rather than around facts.
Why captured events almost never need to evolve
A captured event records a fact. Facts don’t change shape.
The irreducible truth of placing an order — an order came into existence, associated with a customer, at a moment — has been the same truth for as long as orders have existed. There is nothing to evolve.
When a new consumer need emerges, it is not a reason to change the OrderPlaced event. It is a question about what additional facts are now worth capturing.
If a fraud detection system needs to know the device from which the order was placed, that is a new fact being observed: OrderPlacedFromDevice. A new event, capturing a new truth, emitted alongside the existing events. The existing OrderPlaced event is unchanged. Old consumers are unaffected.
If regulatory requirements introduce mandatory data, that is a new process that must now run — a new decision being made, a new fact being recorded. OrderComplianceCheckCompleted. A new event, a new consumer, no changes to existing events.
The pattern is always the same: what looks like a reason to change an existing event is actually a reason to observe a new fact and capture it as a new event. The schema evolution problem dissolves because the existing schema was capturing an irreducible truth that had no reason to change.
Bundling as lossy compression
The OrderPlaced event that contains twenty fields is not capturing one fact. It is bundling many decisions into a single structure.
Consider what actually happened when an order was placed. The customer added an item — a choice. They removed it and added a different one — another choice. They selected a shipping tier — a choice. They applied a discount code — a choice. They reviewed the summary and confirmed — the final commitment.
Each of these is a separate, independent fact. Each could have happened differently. Each is a discrete moment in time.
When you bundle them into a single OrderPlaced event, you are performing lossy compression. The compression is optimised for the consumers you are aware of today. It discards the granularity that future consumers — consumers you haven’t imagined yet — might need.
This is premature optimisation in the most literal sense: optimising for known consumers before knowing what all consumers will need, at the cost of information that cannot be recovered.
The uncompressed version — individual events for each decision — is strictly more informative. You can always derive the bundle from the atoms. You cannot recover the atoms from the bundle.
OrderSubmitted is the last atomic decision — the moment of commitment. It is not a summary of the decisions that preceded it. Those decisions are already in the log. OrderSubmitted says only: at this moment, the customer committed. Everything before it remains visible, queryable, and available to any consumer that needs it.
The order document is not the ground truth
A legally binding order document — a PDF, a contract, a formal record — is immutable. Once issued, it does not change. This might seem like a counterexample: surely the order document is the ground truth?
It is not. It is a materialised projection, frozen at the moment of submission.
The ground truth is the sequence of decisions that led to it. The document is a human-readable rendering of the state at a specific point in time, produced for a specific purpose — legal validity, human readability, physical delivery.
Both are immutable. Neither invalidates the other. They are truths at different levels of granularity, for different audiences, with different purposes.
The document can be derived from the event log at any time and will always be identical. You could choose not to store the document at all and re-derive it on demand — and it would be provably correct because the log is the source. Storing the document is a performance optimisation. Re-deriving it on demand is the proof of correctness.
This is what makes the framework coherent: you never have to choose between the atomic events and the aggregate document. The atomic events are primary. The document is derived. Both exist. Neither changes. The relationship between them is permanent and traceable.
When schema change is genuinely necessary
The argument is not that schemas never change. It is that schema change is almost always a symptom of having drawn the truth boundary incorrectly.
There are genuine cases where the original capture was wrong — not incomplete, but incorrect. A timestamp captured in the wrong timezone. An identifier that turns out not to be globally unique. A field that conflates two things that should have been separate. These are errors in observation, not evolution of needs. The response is a correcting event — OrderTimestampCorrected — which is itself a new fact: a correction was made, at this moment, for this reason. The original event remains. The correction is appended.
And there are cases where the unit of truth was drawn at the wrong boundary. PaymentProcessed that conflates authorisation and capture is an example. Separating them is not schema evolution. It is a better identification of what the atoms actually are.
The practical test is simple: is this change driven by consumer needs, or by a better understanding of what actually happened? If consumer needs, the answer is a new event, not a changed one. If a better understanding of reality, the existing event was capturing the wrong thing, and the correction is warranted.
The practical implication
Event Modeling sessions should not start with consumer requirements. They should start with a different question:
What actually happened? What decision was made? By whom? At what moment? What is the irreducible fact of this occurrence?
The answer to that question is the event. Consumer needs are addressed by composing events, building projections, and creating read models — all of which can change freely without touching the events themselves.
When this discipline is applied, schema evolution becomes rare. Not because schemas are never wrong, but because the things that drive schema change in undisciplined systems — consumer needs, new features, evolving requirements — are handled at the projection layer instead of the event layer.
The tools for schema evolution remain available for the rare cases where genuine correction is needed. They stop being the continuous maintenance burden they become when events are designed rather than captured. And the question that dissolves the problem — what actually happened? — is the question the next part takes up in earnest.