Custom Software Development

Event Sourcing vs. CRUD: Which Should Your Custom Software Use?

Shubham Parmar7 min read
Event Sourcing vs. CRUD — architecture decision guide banner

In short: CRUD stores only a record’s current state — update it, and the previous value is gone. Event sourcing stores every change as an immutable, ordered event, and the current state is derived by replaying them. Default to CRUD; reach for event sourcing only where the history itself has real business value, like financial ledgers, compliance-tracked records, or anything where a past state needs to be reconstructed on demand.

What this means for you

  • CRUD is the right default — most business data (profiles, settings, product catalogs) only needs its current state, not a full history.
  • Event sourcing earns its complexity when audit trails, dispute resolution, or point-in-time reconstruction are an actual business requirement, not a nice-to-have.
  • “CRUD sourcing” — logging state changes with no real domain meaning — is the most common way teams adopt event sourcing and get none of its benefits.
  • Most production systems that use event sourcing apply it to one or two critical domains and keep everything else on CRUD.

Every custom software project with a database eventually asks this question, usually without naming it: do we store what’s true right now, or do we store everything that happened? The answer decides how hard audits, disputes, and “what did this look like last month” requests are going to be — for years after the schema is set.

What each approach actually stores

CRUD

CRUD — Create, Read, Update, Delete — stores the current state of a record in a table or document and overwrites it on every change. A customer’s address, an order’s status, a user’s subscription tier: each lives as one row, and updating it replaces what was there before. If you need to know what the address was six months ago, that information is gone unless you built a separate audit log for it — CRUD doesn’t keep one by default.

Event sourcing

Event sourcing stores every change as an immutable, append-only event — OrderPlaced, AddressChanged, PaymentCaptured — instead of overwriting a row. The current state is never stored directly; it’s derived by replaying the full sequence of events for that entity, or by maintaining a read-optimized “projection” that gets rebuilt from the event log whenever the read model changes. The event log is the source of truth; everything else, including what looks like the “current record,” is a derived view of it.

Why teams reach for event sourcing

The case for event sourcing is almost never about write performance — it’s about what the history itself is worth:

  • A complete, tamper-evident audit trail. Every change is recorded as it happened, in order, instead of being reconstructed after the fact from logs that weren’t designed for it.
  • Point-in-time reconstruction. You can answer “what did this account look like on March 3rd” by replaying events up to that point — useful for disputes, regulatory requests, and debugging state that a snapshot-based system simply can’t answer.
  • New read models without re-architecting. A new reporting or analytics need can replay the existing event log into a new projection, instead of requiring a schema migration and a backfill against data that was never captured at that granularity.
  • Decoupled consumers. Other services can subscribe to the same event stream and build their own view of it, without coupling to your current table structure.

What it actually costs you

None of the above is free, and the costs show up early, not just at scale:

  • Simple reads get harder. “What’s this customer’s current address” is a direct lookup in CRUD and a projection you have to build and maintain in an event-sourced system.
  • Eventual consistency. Projections are usually rebuilt asynchronously, so a read immediately after a write can be stale — a class of bug CRUD systems don’t have to design around.
  • More code, earlier. Event schemas, versioning events as the domain model evolves, and projection-rebuild logic are all work a CRUD table doesn’t require on day one.
  • Unfamiliar to most teams. Fewer engineers have operated an event-sourced system in production, which shows up later as slower onboarding and more careful code review, not just a one-time learning curve.

The “CRUD sourcing” trap

The most common way teams adopt event sourcing and get none of its benefit is what’s sometimes called “CRUD sourcing”: logging generic events like RecordUpdated that just mirror what a CRUD update would have done, field by field, instead of events with real domain meaning like OrderShipped or RefundIssued. It adds all of event sourcing’s real cost — projections, replay logic, eventual consistency — while capturing a log that’s no more useful for audit or reconstruction than the CRUD table it replaced would have been. If your events read like a diff of a row instead of something a domain expert would recognize as a business event, that’s the sign you’ve built CRUD with extra steps.

A practical decision framework

SignalFavors
Regulation or dispute resolution requires a full, defensible history of changesEvent sourcing
You need to answer “what was true as of date X” for past recordsEvent sourcing
Multiple independent systems need to react to the same sequence of changesEvent sourcing
The current value is the whole story and nobody will ask what it used to beCRUD
The team has no production experience operating an event-sourced systemCRUD, or a small pilot domain first
Read performance and query simplicity matter more than historical reconstructionCRUD

The hybrid most systems actually run

In practice, very few production systems are entirely event-sourced. The common pattern is narrower: apply event sourcing to the one or two domains where history has real, demonstrable business value — payments, order lifecycle, compliance-tracked records — and keep everything else, like user profiles, preferences, and catalog data, on ordinary CRUD. Treating the whole system as one approach or the other is rarely the right call; the decision is made per domain, not per project.

This is also a decision that resurfaces during legacy modernization, not just on greenfield builds — see our guide on strangler fig vs. big bang rewrite for how that kind of migration gets sequenced safely.

How we approach this on real projects

On custom software engagements, this is a decision made during architecture design, once we understand which parts of the system actually need a defensible history and which just need to work. Treating every domain as needing full event history adds cost and operational complexity most projects don’t need; treating a compliance-critical ledger as “just another CRUD table” creates a gap that’s expensive to fix once the system is already live.

Frequently asked questions

What is event sourcing?

Event sourcing is a data persistence pattern where every change to an entity is stored as an immutable, ordered event — rather than overwriting a row with its latest state. The current state is derived by replaying those events, or by maintaining a read-optimized projection built from the event log. The event log itself is the source of truth.

How is event sourcing different from CRUD?

CRUD (Create, Read, Update, Delete) stores only the current state of a record and overwrites it on every change — the previous value is gone unless you built a separate audit log. Event sourcing stores the full history of changes as events and derives the current state from them, so nothing is ever truly overwritten.

When should you use event sourcing instead of CRUD?

Use event sourcing when the history itself has real business value — compliance-grade audit trails, financial ledgers, or cases where you need to reconstruct what the state was at a specific point in the past for a dispute or analysis. For most business data — user profiles, product catalogs, settings — CRUD is simpler, cheaper to build, and easier to query, and the history isn't something the business actually needs.

What's the most common mistake teams make when adopting event sourcing?

“CRUD sourcing” — logging generic ‘field updated’ events that just mirror what a CRUD update would have done, instead of events with real domain meaning (like ‘OrderShipped’ or ‘RefundIssued’). It adds all of event sourcing's complexity — projections, replay logic, eventual consistency — without the benefit, since the log doesn't actually capture meaningful business history.

Can a system use both event sourcing and CRUD?

Yes, and most production systems that use event sourcing do exactly this: apply it only to the one or two domains where history has real business value — payments, order lifecycle, compliance-tracked records — and keep everything else, like user profiles and catalog data, on ordinary CRUD. Treating the whole system as one or the other is rarely the right call.

Sources

Need help putting this into practice?

Tech Programmer builds and ships this work for startups and enterprises. Tell us what you are trying to do and we will tell you what it takes.

Related reading