Skip to content

... And Then the Wolf DELETED Grandma

Last week, I had the pleasure of speaking at the Software Architecture Gathering 2025 in Berlin. The conference is organized by the iSAQB (International Software Architecture Qualification Board) and brought together around 400 attendees from numerous countries. My talk, titled "... And Then the Wolf DELETED Grandma," explored why CRUD falls short when modeling real-world processes, and about 120 people joined me in the room to discuss fairy tales, databases, and the limits of our industry's favorite paradigm.

The response was overwhelming. Conversations continued long after the session ended, and many attendees shared similar frustrations with CRUD in their own projects. This post is the written version of that talk: for everyone who couldn't be there, and for those who were and wanted to revisit the ideas.

Golo Roden at Software Architecture Gathering 2025
Golo Roden at Software Architecture Gathering 2025

Once upon a time, there was a grandmother, a wolf, and a little girl in a red hood. You know the story. It's one of the oldest tales in the Western world, passed down through generations, told and retold millions of times. And yet, if you try to tell this story using modern software architecture, something strange happens.

Let's try it. We'll use CRUD (Create, Read, Update, Delete), the dominant paradigm for data management in software development. How hard can it be to model a fairy tale?

A Fairy Tale in CRUD

We start by creating our characters:

Grandmother.create({ name: "Grandma", location: "cottage", status: "healthy" })
Wolf.create({ name: "Big Bad Wolf", location: "forest", status: "hungry" })
RedRidingHood.create({ name: "Little Red Riding Hood", location: "home", status: "carrying basket" })

So far, so good. The story begins. Red Riding Hood walks through the forest. We update her location:

RedRidingHood.update({ location: "forest" })

The wolf runs ahead to the grandmother's cottage. Another update:

Wolf.update({ location: "cottage" })

And then comes the pivotal moment. The wolf eats the grandmother. In CRUD, we have exactly one way to express this:

Grandmother.delete()

The story continues. The wolf disguises himself, waits for Red Riding Hood, and eventually the hunter arrives to save the day. He cuts open the wolf's belly, and out comes... wait. The grandmother is gone. We deleted her. How do we bring her back?

The DELETE Problem

Here's where things fall apart. CRUD gives us exactly three verbs to modify data: Create, Update, and Delete. None of them is "restore." None of them is "rescue." And certainly none of them is "cut open wolf's belly and free grandmother."

You might think: "Well, just create a new grandmother." But that's not the same grandmother. That's a different record, a different identity, a different story. The original grandmother, with her history, her relationships, her identity, is gone. Permanently erased from the system.

The fairy tale can't continue. The hunter stands there with his scissors, ready to be a hero, but the database has already made that impossible. CRUD has broken the story.

And this isn't just an academic problem. This exact scenario plays out in business systems every single day. We just don't notice it because we've become so accustomed to the workarounds.

The Soft-Delete Workaround

The "clever" solution that every experienced developer knows: don't actually delete anything. Instead, add a flag:

Grandmother.update({ isDeleted: true })

Now we can "restore" the grandmother later by setting isDeleted: false. Problem solved, right?

Not quite. Think about what we've done. We used an UPDATE to express a DELETE that actually means "eaten by wolf." The semantic richness of the original event is completely lost. What does isDeleted: true actually mean? Was the grandmother eaten? Did she die of natural causes? Did she move to Florida? Did someone accidentally click the wrong button?

The flag tells us nothing. It's a technical workaround masquerading as a solution. And it gets worse: what if we later need to actually delete the grandmother's data for GDPR compliance? We've already used our deletion mechanism to mean "eaten." Now we need isReallyDeleted, or deletedAt timestamps, or deletionReason enums. The complexity spirals.

We're patching a broken paradigm instead of questioning whether the paradigm itself is wrong.

From Fairy Tales to Business

If CRUD can't handle a simple fairy tale (one of the most basic narrative structures humans have invented), why do we keep using it for complex business logic?

Consider these real-world scenarios:

An order is "cancelled" versus "deleted." What's the difference? A cancellation has business meaning: the customer changed their mind, inventory needs to be restocked, refunds may be processed. A deletion is just... gone. But in CRUD, both might look like the same operation.

A customer is "deactivated" versus "deleted." Under GDPR, there's a massive legal difference. Deactivation means they can't log in but their history remains. Deletion means erasure of personal data. But in many systems, both are modeled as updates to a status flag, or worse, as actual deletions that lose audit history right when you need it most.

A contract is "terminated" versus "expired" versus "cancelled" versus "voided." Each has different legal implications, different accounting treatments, different downstream effects. But CRUD gives us only three verbs to express infinite business realities.

The result? Business language and technical language drift apart. The business says "the customer churned," and IT says "we updated the status field." The business says "the order was fulfilled," and IT says "we set completed to true." The semantic richness of what actually happened gets flattened into generic database operations.

The Blind Spot

Here's the strange thing: almost everyone in software development knows that CRUD has problems. We've all built audit logs to track what CRUD destroyed. We've all implemented soft-deletes. We've all added createdAt, updatedAt, deletedAt timestamps. We've all written change-tracking triggers and history tables.

We build elaborate workarounds to compensate for CRUD's limitations. And yet, we rarely stop to ask: if we need all these workarounds, maybe the underlying approach is fundamentally flawed?

Constant overwriting erases traces. Every UPDATE destroys the previous state. Every DELETE removes evidence. The past becomes invisible, reconstructible only through forensic archaeology of log files (if we remembered to create them, if they haven't been rotated away, if someone has time to parse them).

We're so used to thinking in CRUD that we can't see the cage we've built around ourselves. We optimize the wrong thing instead of doing the right thing.

The Noun Problem

The issue goes deeper than just three verbs. Our entire way of thinking about software is noun-centric.

We model database tables: Users, Orders, Products, Invoices. All nouns. We write classes: Customer, Contract, Employee, Account. More nouns. Our ORMs map objects to rows, entities to tables, things to storage locations. The entire architecture revolves around nouns.

Steve Yegge brilliantly described this problem back in 2006 in his essay "Execution in the Kingdom of Nouns". Almost twenty years later, we're still trapped in the same kingdom.

But here's the thing: stories are told through verbs, not nouns.

"Little Red Riding Hood" is a noun, the name of a character. But "walked through the forest," "met the wolf," "was eaten," "was rescued": those are verbs. Those are the story. Nobody retells a fairy tale by listing the characters. They retell it by describing what happened.

CRUD forces us to see everything through the lens of things. But in the real world, events happen. Actions occur. Decisions are made. Processes unfold. The world runs on verbs, not nouns.

The paradigm shift we need: from "What exists?" to "What happened?"

Event Sourcing: Telling the Story as It Happened

What if, instead of storing the current state of things, we simply recorded what happened?

Let's retell Little Red Riding Hood with events:

GrandmotherWentToBed
  subject: /characters/grandmother
  data: { location: "cottage", time: "evening" }

WolfArrivedAtCottage
  subject: /characters/wolf
  data: { disguise: false }

WolfAteGrandmother
  subject: /characters/grandmother
  data: { eatenBy: "wolf", location: "wolf's belly" }

WolfDisguisedAsGrandmother
  subject: /characters/wolf
  data: { disguise: true, wearing: "grandmother's clothes" }

RedRidingHoodArrived
  subject: /characters/red-riding-hood
  data: { location: "cottage" }

HunterArrivedAtCottage
  subject: /characters/hunter
  data: { tool: "scissors" }

HunterCutOpenWolf
  subject: /characters/wolf
  data: { cutOpenBy: "hunter" }

GrandmotherWasFreed
  subject: /characters/grandmother
  data: { freedBy: "hunter", previousLocation: "wolf's belly" }

The grandmother was eaten, not deleted. That's a fact that happened: immutable, semantically rich, recorded forever. And later, she was freed. Another fact. The complete story is preserved, exactly as it occurred.

Events are verbs in past tense. They describe what happened, not what exists. And that's exactly what stories are made of, in fairy tales and in business.

There's no "undelete" because there's no delete. There's no overwriting because events are immutable. If something needs to be corrected, you add a correction event. You don't falsify history. The original event remains, and a new event records the correction. The audit trail is complete and honest.

The Benefits Beyond Storytelling

This approach, called Event Sourcing, has profound practical benefits:

Audit trail by design. You don't need to bolt on logging as an afterthought. Every change is an event, automatically recorded. Compliance becomes natural, not painful.

Point-in-time queries. "What was the state of this order at 3pm on Tuesday?" With Event Sourcing, you replay events up to that moment and see exactly what was true. With CRUD, you hope someone was running backups.

Debugging through replay. When something goes wrong, you can replay the exact sequence of events that led to the problem. You don't guess; you observe. Every bug becomes reproducible.

Business language in the code. Events like OrderShipped, PaymentReceived, ContractTerminated speak the language of the domain. Business and IT finally speak the same vocabulary.

Asking new questions of old data. A year from now, you realize you need to analyze customer behavior patterns you never thought to track. With CRUD, the data is gone, overwritten a thousand times. With Event Sourcing, every event is still there, waiting to answer questions you haven't thought of yet.

Event Sourcing as the Foundation for AI

There's another reason Event Sourcing is becoming increasingly relevant: artificial intelligence.

The AI revolution isn't just about better models. The bottleneck increasingly isn't the algorithm; it's the data. Garbage in, garbage out has never been more true. The most sophisticated model in the world can't extract insights from impoverished data.

And here's the problem: most business data is impoverished. CRUD systems store snapshots: the current state, overwritten countless times. Snapshots hide causality. They show you what is, but not how it became that way or why.

Event Sourcing provides exactly what AI needs:

  • Complete history instead of snapshots: every decision, every change, every action
  • Causal relationships instead of isolated data points: what led to what
  • Semantic clarity instead of ambiguous flags: CustomerChurned is clearer than status: "inactive"
  • Temporal patterns instead of static values: behavior over time, not frozen moments

We explored this recently in Event-Driven Data Science: EventSourcingDB Meets Python and Pandas. The analysis revealed patterns invisible to traditional approaches: how people actually behave, what sequences lead to what outcomes, what the data really tells us when we preserve the full story.

The future doesn't belong to those with the most sophisticated AI models. It belongs to those with the richest data foundations. Event Sourcing is how you build that foundation. If you're curious about the intersection of Event Sourcing and AI, visit eventsourcing.ai.

The Happy End

The wolf didn't "delete" the grandmother. He ate her. That distinction matters.

In fairy tales, as in business, what happened is not the same as what exists now. The journey matters as much as the destination. The process matters as much as the outcome. The verbs matter as much as (more than!) the nouns.

Event Sourcing lets us tell stories the way they actually happened. It preserves meaning. It maintains history. It speaks the language of the domain. And it prepares us for a future where AI will demand richer, more complete data than CRUD can provide.

The discussions at the Software Architecture Gathering confirmed what I've seen in countless projects: developers intuitively understand that CRUD is broken. They've just been waiting for permission to think differently.

Where to Go From Here

If you're ready to explore Event Sourcing, the Introduction to Event Sourcing explains the core concepts, and the Getting Started guide will have you writing your first events in minutes.

If you're curious how Event Sourcing can transform your data architecture, whether for compliance, analytics, AI, or simply for building software that models reality more honestly, we'd love to hear from you. Reach out at hello@thenativeweb.io.

Because your business isn't a collection of tables. It's a story. And stories deserve to be told properly.