We Hoard Event Sourcing for Our Tools and CRUD Our Users
Spend a morning the way most of us do. You git commit — an immutable, content-addressed, append-only record. You push, and a pipeline fires on the event. You git blame to find when a line changed and git bisect to find which commit broke the build, then git revert to undo it without losing the history of having done so. Your container image is a stack of immutable layers. Your lockfile pins exact versions, and its diffs live forever in the repo. Your dashboards replay a stream of traces and spans.
Every tool you trust with something important keeps a perfect, append-only record of what happened.
Then you open your editor and write:
UPDATE users SET email = ? WHERE id = ?;
…and the previous email is gone. Not archived. Not superseded. Gone. No blame, no bisect, no revert. If anyone ever asks “what was it before, and who changed it, and why?”, the honest answer is: we threw that away.
We run our entire profession on event logs — and ship our users CRUD.
Look at your tools
This isn’t a niche observation. The load-bearing parts of modern software are saturated with event sourcing:
- git is an event-sourced filesystem — a DAG of immutable commits; the working tree is just a projection of the latest one.
- CI/CD is event-driven by definition: pipelines are triggered by commits and merges, and every run is logged.
- GitOps took the one thing everyone still called “static” — configuration — and wrapped it in commits, reviews, rollbacks, and drift detection. The desired state is a projection of a git history; reconciliation compares that projection to reality. That’s event sourcing wearing an ops hat.
- Container images are immutable, content-addressed layers. Package managers pin and preserve. Kafka is, literally, “the log as a service.” Redux reduces a stream of actions into state, and its devtools let you time-travel by replaying them.
The pattern recurs everywhere correctness, audit, or undo actually matter. We didn’t plan it. We kept reinventing the append-only log because it’s the honest representation, and we were willing to pay for honesty wherever the stakes were high enough to force the issue.
The part that should sting
Here is the exhibit that ends the argument: the database you’re using to implement CRUD is itself event-sourced.
Every write to Postgres, to InnoDB, to essentially any serious engine, goes to an append-only write-ahead log before it touches a table. Durability depends on that log. Crash recovery replays it. Replication ships it. Point-in-time recovery rewinds it. The database keeps a flawless, ordered history of everything that has happened — for its own survival.
And then it hands your application a mutable table with the history stripped off.
So when you ask “we event-source our tools, why not our output?” — the uncomfortable truth is that the event log already exists, one layer down. CRUD doesn’t avoid the log. It just denies your domain access to the one the database is already keeping for itself.
We hoard event sourcing. We keep the history — git, the WAL, the audit pipeline, the trace store — for the things we care about: our code, our uptime, our ability to debug at 3 a.m. And we give the people who use our software a destructive UPDATE. You would be appalled if git did to your code what your code does to your users’ data every day.
CRUD isn’t wrong. The lie is what we say about it.
Let’s be fair, because the zealots have made this conversation tiresome. CRUD is not a sin. It is lossy, and lossy is frequently the correct trade — nobody should event-source the list of ISO country codes, and plenty of admin screens have no business carrying a decade of provenance. Event sourcing has real costs: eventual consistency, projection rebuilds, more moving parts, more storage.
The lie isn’t CRUD. The lie is CRUD presented as the natural, default shape of data — as “just how databases work” — when it is actually a deliberate decision to discard history. “Static data” isn’t a category of thing in the world. It’s a process whose history you chose not to keep. A balance is every debit and credit, folded; double-entry bookkeeping has stored the journal and derived the balance for seven centuries. A user’s current address is the last stop in a sequence of moves. The snapshot is never the truth — it’s the most recent projection of a truth we usually decline to write down.
CRUD won not because it’s right but because it’s the path of least resistance: the relational model and the ORM made the snapshot the easy default, and the cost of the choice — we can no longer answer “what happened?” — is invisible at the moment you make it. It fails silently. The bill arrives later, as audit tables bolted on after a compliance scare, as status columns and deleted_at flags and history tables that badly re-derive the event log you could have had for free, as debugging sessions that are really archaeology. The reflex survives precisely because it’s locally convenient and globally lossy.
So model the process, not the snapshot
The fix is not “add an audit table.” It’s to stop pretending the snapshot is primary. Store what happened; project the snapshot from it. The events are the source of truth; the “current state” is a read model you can throw away and rebuild.
The hard part of event sourcing was never the idea — it’s that the canonical literature reads abstract: bounded contexts, ubiquitous language, aggregates, projections. Correct, and easy to bounce off. What makes it land is a metaphor everyone already owns: an office. A request lands on a desk. A clerk pulls the dossier, reads the slips inside (the events so far), decides whether the change is allowed, files a new slip if it is, and passes the folder on. “Rebuilding state” is just reading the dossier. That’s the whole model, and you didn’t need a textbook to picture it. (We call the structure that falls out of this the D4 model — Division, Department, Desk, Dossier — but that’s another post.)
Why not its output?
Because, until recently, giving your domain a real event store meant building one. That’s the gap ReckonDB closes: an append-only, Raft-replicated event store for your application’s data — the same kind of log your infrastructure already runs on, finally available to the part of the system your users actually touch. Your database keeps a WAL for itself. This is the WAL for your business.
You already believe in this. You stake your code, your deploys, and your uptime on it every day. The only question left is why your users don’t get the same courtesy.
Event sourcing isn’t a technique you adopt. It’s the substrate you’re already standing on. CRUD is what you build when you forget that.
Want the human version of the architecture this implies? See the methodology — the dossier, the company model, and why your tables are hiding a process.