Altamar Labs
Back to Blog
architecture· 3 min read

Designing for auditability from the data model, not as a reporting layer

When a business needs to answer 'what exactly happened to this,' the answer can't depend on reconstructing history afterward. It has to be in how the entities were modeled from the start.

There's a question that, in certain businesses, gets asked more often than any engineering team anticipates: "what exactly happened to this?" Not "what's the current state," but the full chain of events that led to that state. When the data model wasn't designed with that question in mind, answering it turns into archaeology across scattered logs, at best, or an impossible reconstruction, at worst.

Why current state isn't enough

Most data models are designed to answer "how is this right now": an order has a status, a package has a current location, an account has a balance. That information is necessary, but it's fundamentally different from "how did it get to be this way," which requires keeping the full sequence of transitions, not just the final result.

A business that moves physical goods through several stages (a provider hands something off, it goes through an intermediate operation, gets grouped into a shipment, travels on a route) needs to be able to reconstruct that full chain after the fact, whether to resolve a dispute with a customer, to meet a billing requirement, or simply because something went wrong and someone needs to understand exactly where it happened.

The difference between storing state and storing history

Modeling only current state, with a column that gets overwritten every time something changes, is simpler to implement and enough for most day-to-day functionality. But it's a decision that, made without thinking, discards information that later turns out to be impossible to recover: if a package's status changed three times in a day, a model that only stores current state has no way of knowing those three changes happened, or when, or why.

The alternative isn't complicating every table with a generic change log. It's identifying, from the domain design, which entities a business is actually going to need to audit, and modeling those specifically as a chain of immutable events instead of a mutable record. Each step in that chain (received, batched, dispatched, delivered) becomes a fact that already happened and doesn't get overwritten, instead of a status field that only reflects the present.

The cost of not doing it in time

Adding auditability later, on top of a model that only ever stored state, is far more expensive than designing it from the start. It isn't a trivial schema change, it's a change in how the entire system thinks about its own entities: from "this is what exists now" to "this is everything that happened to get here." Migrating that way of thinking midway through a product's life, with data already in production and no prior history to reconstruct, is practically impossible: history that was never recorded can't be recovered retroactively.

The lesson

Auditability isn't a feature you add. It's a property of the data model that gets decided, or lost, the moment the tables are designed. For businesses where traceability isn't optional (logistics, payments, any regulated system), the right question when modeling an entity isn't just "what fields does it need," it's "will someone, at some point, need to reconstruct how it got here." If the answer is yes, that entity needs to be a chain of events, not a row that gets overwritten.