Never change or delete a money record. If a payment, invoice or stock movement is wrong, fix it by adding a new entry that cancels it, then add the correct one.
That way your history stays complete. Disputes become provable, mistakes become easy to trace, and silent fraud gets much harder.
This guide explains the idea in plain words and shows the database pattern with real SQL. It also explains why this matters even more for businesses in Algeria.
Why should you never edit a money record?
Because an Edit button on money destroys the one thing that makes a financial system useful. That is the ability to answer what happened, when, and who did it.
Think of a school report card. If a teacher could rub out a mark and write a new one with no trace, nobody could ever trust a report card again.
So schools cross out and initial, or issue a correction. The old mark stays visible.
Clients still ask for Edit buttons all the time, on invoices, stock movements or payments. It sounds reasonable: a number is wrong, so fix it.
But the moment a money row can be silently changed, your database stops being a record and becomes an opinion. It only shows the last thing someone typed.
What is an append only ledger?
An append only ledger is a table where money movements are only ever added, never changed or deleted.
Accounting solved this long before computers. In double entry bookkeeping, every movement is written twice, as a debit on one account and a credit on another, and the two must balance.
When something is wrong, you don't rewrite history. You write a new entry that cancels the old one, then write the correct one.
In a database it works the same way:
- Rows are inserted. They are never updated and never deleted.
- A balance is not a stored column. It is calculated by adding up the movements.
- A mistake is fixed with a compensating transaction (a new entry that cancels the wrong one).
Example: a customer was charged 4,000 DZD instead of 3,000 DZD. You don't change the row to 3,000. You add a reversal of 4,000, then add the correct 3,000.
Anyone reading later sees the charge, the mistake, the reversal and the correction, each with a time and a person.
What does an append only ledger give you that an Edit button can't?
| Editable row (the Edit button) | Append only ledger | |
|---|---|---|
| Fixing a mistake | The old value is overwritten and gone | A reversal is added, both stay visible |
| "What did this say last week?" | Impossible to answer | One query by date |
| Who changed it | Unknown | Every row records its author |
| The same payment sent twice | Charged twice | Rejected by a unique reference |
| Two people writing at once | One change silently lost | Two rows, nothing lost |
| The balance | A number that drifts out of sync | Calculated, always consistent |
| A dispute with a customer | Your word against theirs | A dated list of events |
| An employee hiding money | Edit the number and move on | Needs a visible reversal |
The last row matters most in practice. An editable money table lets a dishonest person change a number and leave nothing behind to find.
Why does this matter even more in Algeria?
The law already expects it for merchants' books. Article 11 of the Algerian Commercial Code says the journal and the inventory book are kept in date order, "sans blanc, ni altération d'aucune sorte", without blanks or alterations of any kind (Commercial Code, Book I). Software that lets anyone rewrite past amounts works against that principle.
Cash moves far from your server. With cash on delivery, money and products change hands at the door, through a courier who does not work for you. Between "order placed" and "cash in hand" there are many events: no answer, a second attempt, a refusal, a return, a weekly payout from the courier minus fees. Store all that as one row with a status you keep overwriting, and when the courier's payout doesn't match your total, you have nothing to check.
Partial payments are common. A customer pays 5,000 DZD of a 12,000 DZD order at the door, then 4,000 DZD a week later in the shop, and the last 3,000 DZD never comes. On an editable row, someone types 9,000 and the story is lost. On a ledger, you have three receipts and an outstanding balance that calculates itself.
The paper notebook was already a ledger. In a paper cahier you can't cleanly erase. A crossed out line with a correction next to it is an append only ledger written in ink. Replace that notebook with software that has an Edit button, and you give the owner something worse than what they had.
How do you build an append only ledger in a database?
1. Model money as events, not as a state. Store amounts as whole numbers in centimes, because decimal numbers in code cause rounding errors with money.
create table ledger_entries (
id uuid primary key default gen_random_uuid(),
account_id uuid not null references accounts(id),
amount_cents bigint not null check (amount_cents > 0),
direction text not null check (direction in ('credit', 'debit')),
currency char(3) not null default 'DZD',
-- What this movement is about: an order, a courier payout, a refund.
reference_type text not null,
reference_id uuid not null,
-- The same outside event can never be written twice.
external_ref text unique,
-- A reversal points at the entry it cancels. NULL for normal movements.
reverses_id uuid references ledger_entries(id),
actor_id uuid references profiles(id),
memo text,
created_at timestamptz not null default now()
);
create index on ledger_entries (account_id, created_at desc);
create index on ledger_entries (reference_type, reference_id);
2. Block changes and deletions in the database itself. Don't rely on the app behaving well. A trigger (a rule that runs automatically inside the database) can refuse every update and delete (PostgreSQL documentation).
create or replace function ledger_is_immutable() returns trigger as $$
begin
raise exception 'ledger_entries is append only: write a compensating entry instead';
end;
$$ language plpgsql;
create trigger ledger_no_update before update or delete on ledger_entries
for each row execute function ledger_is_immutable();
A rule the database enforces is a rule. A rule only the app enforces is a suggestion.
3. Calculate the balance instead of storing it.
select coalesce(sum(case when direction = 'credit' then amount_cents
else -amount_cents end), 0) as balance_cents
from ledger_entries
where account_id = $1;
If that gets slow on a big table, save a snapshot with the last entry it covers and add up from there. Cache the number, but never let the cache become the truth.
This pattern is close to what engineers call event sourcing. It means storing every change as an event, not only the latest state (Martin Fowler, Event Sourcing).
4. Give the client the button they asked for, and make it safe. Don't lecture them about accounting. Label the button "Correct", let them type the right number, and have it write a reversal and a new entry behind the scenes. They get their fix, you keep the history.
5. Show the history on screen. A corrected invoice should clearly show the original, the correction, the time and the person. That trail is what settles a dispute in your client's favour.
What mistakes do developers make with money in software?
- Storing the balance as a column and updating it. Two updates at once can silently lose one.
- Using decimal numbers for money. Use whole numbers in centimes.
- Protecting the ledger only in the app. Block updates and deletes in the database.
- No unique reference on outside events. A courier payout imported twice gets counted twice.
- Hiding corrections from users. The visible trail is the feature that ends disputes.
What should you do this week?
- Today: search your system for any screen that lets someone change a past payment, invoice or stock movement.
- This week: list every table where a balance is stored instead of calculated.
- This month: add a ledger table for new money movements, with the trigger above.
- Then: turn every "Edit" on money into "Correct", writing a reversal and a new entry.
- Finally: show the correction history on invoices and customer accounts.
For a real example of why a paper trail matters in a small business, read why garages need immutable ledgers. And if your business still runs on WhatsApp and Excel, start with when WhatsApp and Excel stop working.