Articles

Who changed this? The audit trail nobody asks for until it matters

The screen shows the new price, not the old one or who set it. What an audit record must hold, and how to start a trail where nothing was kept.

By Anes Hamdaoui6 min read
  • audit
  • data
  • operations
All articles

A price moved, a stock adjustment appeared, an invoice was cancelled, and the screen shows the new value only — no before, no name, no time, no reason. The audit trail answers "who changed this", and it is among the cheapest things to build into a business system and the most expensive to lack. It is not a developer's log, and treating it as one is why the question is asked two years too late.

What has to be in an audit record?

Five things, and any one missing turns the other four into decoration.

  • Who — an identity that resolves to a person, not an account several people hold.
  • When — the server's clock, in one timezone, written at the moment of the change, not the browser's clock.
  • What changed — the field, the value before and the value after. Not a copy of the row; the row tells you what it looks like now, not what moved.
  • Why — the reason a human gives when the change is a decision: a discount, an adjustment, a cancellation.
  • On which record — the document and its version, plus the path the change came through: this screen, an import, an integration.

Why does "who" decide whether the record is worth anything?

Because a shared login makes every entry true about an account and useless about the business. "Admin changed the delivery price at 16:12" is a sentence you cannot act on: you cannot correct the behaviour, defend the change to a customer, or hand the record to an auditor.

Service accounts fail the same way in a quieter manner. When imports, a courier integration and a nightly job all write as "system", every bulk change looks identical, and the one question worth asking — which of my sources moved this number — has no answer. Name them per source: "import: supplier price list", "courier: returns file".

How is a log for debugging different from a record for an argument?

A debug log exists for the person who wrote the code, this week. An audit record exists for someone outside the team, next year. They are not the same artifact.

Debug log Audit record
The question it answers Why did this request fail Who changed the price on this invoice
Who reads it A developer, within days An owner, a customer, an auditor
How long it lives Days, then rotated away As long as the record it describes
What may happen to it Deleted, edited, sampled Appended, never changed

A log kept for fourteen days cannot answer a question about March, and an audit table the price-changing administrator can also edit proves nothing.

What do you do about records that already exist without a trail?

Start the trail now, and mark the boundary honestly. An attempted reconstruction is worse than a gap: it produces a confident record that is wrong.

Three things work. Backfill only where a document proves the value: a printed invoice, a signed delivery note, a numbering sequence. Print the start date at the top of the reports covering the earlier period, so the gap is visible rather than discovered. And add a note field to manual adjustments, so every correction from today carries a reason.

When a dispute about an older record arrives, the honest answer is short: the system did not keep it, here is what the paper says, and here is the point from which it does.

Why does a trail nobody reads cost more than it saves?

Two costs, and neither is storage. The first is coverage: a trail on three tables out of five means every argument lands on the two that are missing, and each new write path — an import, a Friday script, an admin tool — is a hole unless added deliberately.

The second is the illusion of control. A record nobody queries has never prevented a change and never settled a dispute. What turns it into a control is a screen someone opens: the last fifty price changes, this month's stock adjustments, the invoices cancelled last week — the name, the reason and the before-and-after on one line. One question in a monthly meeting keeps it alive, and it is the only thing that makes the writing worth doing.

How do you add a trail to a system that has none?

  1. Choose the tables that carry money, stock, price, customer identity and status. A trail over the whole database is a project; over those, it is a week.
  2. Write down the questions you would need to answer, taken from arguments that actually happened last year rather than from imagination.
  3. Record the change in the same transaction as the change itself. If the trail can be written without the change, or the change without the trail, one of them will be missing on the day that matters.
  4. Store the before and after per field, alongside a row identifier. A row snapshot answers a different question.
  5. Require a reason where a person made a decision: a discount, an adjustment, a cancellation. Offer a short list plus free text; a reason demanded on every keystroke becomes "ok" on every line.
  6. Close the trail's table to updates and deletes for everyone, the administrator included.
  7. Build one screen with three filters — by person, by record, by period — and open it with the person who will use it before calling the work finished.
  8. Prove it: change a price on a test record, then find the entry that names you, the old value and the new one.

Where this goes wrong

  • Only the web application writes. Then support runs a fix directly in the database and the most suspicious change in the month has no entry. Every write path counts, including imports and scripts.
  • The trail stores the new row and calls it history. It cannot say what changed, so the argument it was built for continues.
  • Times come from the device. Two laptops with different clocks put the same afternoon in a different order.
  • Personal data accumulates in the trail forever. A phone number or a card fragment copied into a table nobody prunes outlives the record it describes. What may be kept and for how long is your auditor's to state, not ours — implement what they specify.
  • A trail covering prices but not stock. The adjustment is where the unexplained loss appears, which is where the trail was wanted.

What this does not cover

This is not a backup: a restore returns the state of a moment and erases the history in between, the opposite of what a dispute needs. It is not security logging either — failed logins, blocked requests and permission changes are a separate record with a different reader. Nor does any of this prove your trail is complete; only a test on each write path does.

We build systems that keep the before and after of every change a person makes, and one screen where a manager can open it on a Monday and see who touched a price.

Services in this article

Start a project

Have something to build?

One short brief is enough. You get a written scope and a fixed quote before anything starts.