Engineering & deliveryField notes

Tamper-proof logging: why database logs do not count in an audit

3 min read

The sentence always comes in the same tone, half surprised, half annoyed: “But we log everything.” Usually that is true. There is a table, it is called audit_log or history, it gets filled on every change, and it is impressively large.

And still the conversation with the auditor ends with that log not being accepted as evidence.

The distinction that matters

A log is a record. Evidence is a record nobody can change unnoticed.

That sounds like a nuance and is the entire point. A log table in the same database the application writes to is reachable with the same permissions as the business data. Whoever can change the record can usually also change the entry describing the change. An auditor has to assume exactly that is possible — not out of distrust in your team, but because otherwise their own audit would be worthless.

Four properties that make the difference

Immutability. Entries are appended, never overwritten, never deleted. No UPDATE, no DELETE — not for administrators either, and not “just this once, because of the migration”.

Completeness. Every business-relevant change produces an entry, in the same transaction as the change itself. A log that can fail on error has gaps exactly where things got interesting.

Domain meaning. The entry describes what happened in business terms, not which column was written. “Credit limit raised to 50,000, approved by sales management, based on the annual agreement” is evidence. “UPDATE customer SET limit=50000” proves something happened, not why.

Attribution. Every entry carries the identity that applied at the time of the action — not the user behind that ID today, and certainly not a shared technical account.

A test you can run yourself

Take any case from last quarter and answer three questions without asking a developer:

  1. Who changed it last, and on whose approval?
  2. What did it look like on a specific earlier date?
  3. Could anyone manipulate those two answers without it being noticed?

If one question stays open, you have a record, not evidence. The provability check asks these questions systematically across ten typical audit situations.

Why this is surfacing now

Two developments meet. Evidence duties are getting concrete: NIS2 requires not just measures but their effectiveness, and the reporting deadlines leave no time for manual forensics. At the same time software is built faster and with fewer handovers, so less documentation appears as a by-product — a point the consolidation of engineering roles makes visible.

Both point the same way: whatever the system does not capture, nobody will reconstruct later.

How to retrofit it without rebuilding everything

The expensive option is a full rebuild. The workable one starts with a selection: almost every company has one to three processes where the history question actually gets asked — approvals, prices, permissions, payments. Those get an immutable, domain-worded history. The rest stays as it is.

Technically the direct route is an append-only store whose write path is separated from the application, plus events named in business terms. That is where event sourcing comes in — not as an architectural fashion, but because it makes exactly this kind of history the default. It is not the only way, and we say so when it is oversized.

What you end up with

Not more logs. The ability to answer an uncomfortable question in minutes instead of person-days — in a form that still holds when someone doubts it. What that looks like in practice is shown in “Who promised what?”, where exactly this gap created half a year of effort.

Back to insights

Articles on this topic