Event Stream Versioning
Transforming an old event version into the current shape when it is read, allowing a stored event history to be interpreted by evolved code.
In event sourcing the event log is immutable and permanent, so events written years ago must still be readable by today's code. Since events cannot be rewritten, the adaptation happens on read.
An upcaster is a function from version N to version N+1, chained so that a very old event passes through several. Adding an optional field with a default, renaming a field, or splitting one event into two are all handled this way.
Two costs that must be understood before committing to the pattern:
Upcasters are permanent. Every historical version needs its transformation maintained forever, and the chain grows. Some teams periodically rewrite the log into a new stream with current-shape events — which is a significant operation and loses the strict immutability guarantee, so it is done rarely and deliberately.
Semantic changes cannot be upcast. If the meaning of a field changed rather than its shape, no transformation recovers the intent of old events, and the honest answer is a new event type.
The related mechanism is the snapshot: rebuilding state from thousands of events is slow, so a periodic materialised state is stored and only subsequent events are replayed. Snapshots are derived and disposable — they must always be reconstructible from the log, or the log has stopped being the source of truth.