Building an audit trail that survives an audit
The data-model patterns behind a credible audit trail — shadow table, temporal tables or event sourcing — and the trade-offs of each.
The data-model patterns behind a credible audit trail — shadow table, temporal tables or event sourcing — and the trade-offs of each.
This article is the perspective of an engineer who has worked under these requirements, not regulatory advice. For compliance decisions, consult your quality/regulatory specialist.
In an earlier article I described what the regulation requires from an audit trail. This one is the part you actually write: what the data model looks like.
The difference between an audit trail that passes an audit and one that fails is not the volume of information. It is whether you can convincingly answer a single question: “how do we know this record was not modified afterwards?”
The useful minimum for each entry:
The critical part is “the old value”. An audit trail that only says “field X was modified at time Y” does not let you reconstruct state. And reconstruction is exactly what you will be asked for.
| Pattern | How it works | When it is worth it |
|---|---|---|
| Shadow table | A parallel _audit table, written by a trigger or in the data-access layer |
Simplest to add; good when only a few entities are regulated |
| Temporal tables | The database keeps row history automatically (system-versioned) | Low cost, history kept by the engine — but it captures neither who nor why |
| Event sourcing | Current state is derived from an immutable stream of events | Most powerful; high architectural cost — rarely justified for auditing alone |
Watch the middle row, because it is the trap: temporal tables record SysStartTime/SysEndTime and nothing about the actor. To satisfy the “who” requirement above, you still need an identity column written on every modification path — exactly the fragile pattern I warn about below. On top of that, anyone with ALTER rights can switch versioning off, edit the history and switch it back on, so “guaranteed by the engine” is too strong without tight permissions.
In practice, for business systems, temporal tables usually give the best effort-to-benefit ratio — provided you add attribution, and the shadow table remains the pragmatic option when only part of the model is regulated. Event sourcing should be chosen for domain reasons, not to tick a compliance box.
An audit trail is credible if it is hard to falsify and visibly hard to falsify.
TRUNCATE, bulk loads and explicitly disabling the trigger all get around them — and under regulation the trigger is itself code that must be verified.app_user, the audit trail attributes nothing. The user’s identity must be carried down to the layer that writes.Optional but valuable: hash chaining (each entry contains the hash of the previous one). It does not prevent modification, but it makes modification detectable — provided you anchor the chain outside the database (a periodically published digest, WORM storage). Without an anchor, anyone who can rewrite a row can recompute the entire chain, and detectability is zero.
Here is the real tension: the regulation demands retention, while GDPR demands minimisation and, sometimes, erasure.
What works in practice:
Worth knowing that GDPR provides exceptions to erasure where processing is necessary for compliance with a legal obligation (Art. 17(3)(b)) — precisely the regulated-retention case. There is no universal answer; it is a decision made together with legal. But if you wrote raw sensitive data into the audit trail from day one, your later options are very limited.
An audit trail roughly doubles write volume, often more — a shadow table holding old and new values, plus its own indexes, easily exceeds 2×. What helps:
The last one is underrated: an audit trail you cannot query quickly is effectively unusable during an inspection.
If you are working on a system where you need to prove the history of your data and want a second opinion on the model, let’s talk.
Capgemini — client Waters Corporation
Software Engineer (contract)
Jun 2022 – May 2023
Cerner Corporation
Software Engineer
Oct 2016 – Oct 2019
If you are building in this space, let us talk for 30 minutes.
Book a call