In August 2026 I sent my AI assistant a screenshot from one of our profit screens and one angry question: how can expenses increase profit? The screen showed a gross profit, then a line of allocated expenses with a minus sign in front of it, and a net profit that was higher than the gross profit. Expenses had been subtracted as a negative number, so they were added.
The cause was a cancelled journal entry. The system marks the original as "reversed" and posts a mirror entry that cancels it. Every report was counting only "posted" entries, so the original disappeared and the mirror stayed. Half of a pair was being counted, and half of a pair is always a number with the wrong sign.
I am a doctor, not an accountant. When I started KashBoss I did not know what a debit was. I learned accounting by building an accounting system with an AI assistant, one question at a time.
The short answer
Double-entry accounting records every event twice, once where value came from and once where it went, so total debits always equal total credits. To build or check a business app, learn five things: the chart of accounts, journal entries and reversals, the trial balance, control accounts compared with their detail, and one rule: write the journal entry in the same database transaction as the money movement.
Double entry in one picture
Imagine the company's value kept in labeled buckets: Cash, Customers who owe us, Inventory, Suppliers we owe, Sales, Cost of goods sold. Value never appears from nowhere. It moves between buckets. The two sides are called debit and credit. Do not look for meaning in the words. They are names for the left and right columns.
A cash sale of 100: debit Cash 100 and credit Sales 100. At the same moment the goods cost us 70: debit Cost of goods sold 70 and credit Inventory 70. In each event, debits equal credits.
The list of buckets is the chart of accounts. One event written as debits and credits is a journal entry. A posted entry is never deleted. If it was wrong, you post a reversing entry, the same lines with the sides swapped, so the pair adds up to zero and history stays visible.
The trial balance lists every account's debits and credits. If the totals differ, an entry is broken. But a balanced trial balance only proves each entry balances inside itself. It cannot tell you an entry was written twice, written to the wrong bucket or never written. A control account, such as the total customers owe us, should equal the sum of its detail, the individual customer statements. The most useful check in any business system is whether each control account equals its detail.
How we did it in KashBoss
Let the database write the journal
In the early versions the app saved an invoice, then made a second call to write the journal entry. If the second call was interrupted by a lost connection or a closed tab, the money moved and the books stayed empty. These gaps look random, which makes them dangerous. The rule now: every operation that moves money writes its journal entry inside the same database transaction. A transaction is all or nothing. Either the payment, the cash movement, the statement line and the journal entry all exist, or none do.
A second trap hid inside "it returned success". The function that recorded cashier expenses looked up the current fiscal period using a flag marked "current", set once when the year was created and never moved forward. After a company closed some months, no period carried the flag. The check quietly failed, the function skipped the whole journal block and returned success. For weeks, expenses took real money from the drawer and left no trace in the books. The fix finds the period by the expense's date and logs failures loudly instead of swallowing them.
A button on screen is not evidence
Our trial balance screen has a button, shown when totals differ, that says "fix the trial balance". I asked in August 2026 whether it worked. It did not. It created a new entry with a debit and a credit of the same amount on one account, and a new balanced entry adds the same amount to both totals, so the difference could not move. The real fix is completing the broken entry itself. We tested the corrected logic on real data inside a transaction that undoes itself, and the difference went to exactly zero.
A balanced trial balance can still be wrong
Two of our worst accounting bugs never unbalanced anything. An opening balance import saved each amount twice, once as a journal entry and once in a column on the account, and three screens added both. Every balance was doubled, and the trial balance stayed balanced because both sides doubled equally. The other was the reversal bug from the opening.
One month a customer's books showed a loss, with a large amount sitting in account 511, which the chart labeled "Purchases". The AI concluded the system was charging every purchase to the month it was bought, and said that buying in one month and selling in the next explained the loss. I did not know enough to argue, but something felt wrong, so I asked what put purchases into that account in the first place. Nothing had. Account 511 was cost of goods sold. Purchases go to inventory, and only sales move 511. The label was misleading and the explanation I received was completely wrong.
Before drawing any conclusion about an account, break its balance down by the type of entry that created it: sales, purchases, returns, adjustments. As a non-accountant, my most useful skill turned out to be asking "what put this number here?" until the answer made sense.
Rules that must never be lost go on the table
We built FIFO cost layers (first in, first out, where the oldest goods leave first at their own cost) for inventory. Three months later we measured and found it had quietly stopped working. The purchase function had been rewritten several times for other features, and the line that opened a cost layer was lost. We moved the logic into triggers on the table of stock movements: any incoming movement opens a cost layer, any outgoing movement consumes the oldest layers. Because the rule lives on the table, no future rewrite of a function can lose it.
Use this prompt to check the control accounts.
Chapter 13 of the book adds period closing and the profit-report reconciliation.
Key takeaways
- Double entry means value moves between buckets, and debits always equal credits.
- A balanced trial balance proves less than you think, so reconcile control accounts with their detail.
- Write the journal entry inside the same transaction as the money, or you will have money without books.
- Put rules that must never be lost, like FIFO cost layers, on the table itself.
- Ask "what put this number here?" until the answer is clear.
Frequently asked questions
What is double-entry accounting in simple terms?
Every event is recorded twice: once for where value came from and once for where it went. Debits and credits are only names for the two columns, and their totals must always be equal.
Does a balanced trial balance mean my books are correct?
No. It only shows each entry balances within itself. An entry written twice, sent to the wrong account or never written at all can still leave it balanced, so also compare control accounts with their detail.
Why should the journal entry be saved in the same transaction as the payment?
If they are separate steps, an interruption can leave the money moved but the books empty. A single transaction is all or nothing, so either both exist or neither does.