Back when I was studying the Double Entry (DE) system felt like "why why why?" because it felt like an overly-complicated way of performing transaction and storing financial records. But now I feel like it's something that can't be missing.
Also take in mind that Double Entry was invented 400 years before computers. However even with computers, it's still useful for its properties of guaranteeing transactions make sense.
The best way to explain it from a programmer perspective is CRC and Error Check Codes.
Imagine we had to store a value in ECC RAM, but we have to manually calculate the ECC bits.
For the sake of simplicity I'm going to deviate a bit from how real RAM works, and assume the following:
- Our RAM can only store 8-bit values.
- Instead of using regular ECC, we store an 8-bit value of CRC-8.
- We can do math on RAM directly, and on the ECC too.
So image I have to store the value 0x05 to RAM. Its CRC is 0x2E. If I were to write 0x15 to RAM but with CRC = 0x2E, it'd be an error.
ECC is doing its job by ensuring the value can't be altered unless it makes sense.
Now we want to subtract 1 from our records. So we'd do:
0x05 - 0x01 = 0x04
BUT! Now the CRC wouldn't match. Because our record says 0x04 but CRC 0x2E doesn't match.
The CRC-8 for 0x04 is 0x01 The solution is to do math on the CRC directly as an atomic operation:
0x05 - 0x01 = 0x04
0x2E - 0x2D = 0x01
NOW it matches! The important key that I want to highlight is that there is a unique mathematical way to ensure the transaction is correct:
- If I want to subtract 0x01 from 0x05; I MUST subtract 0x2D from 0x2E
- I can obtain the value 0x2D (an unknown) mathematically.
OK now that I've explained this, the Double Entry isn't as complex as calculating a CRC, but it's more like RAID1 that keeps an extra copy (Until it isn't, more on that later)
So the way DE works is that we have a Debit and a Credit columns. AND THEY MUST MATCH. We say they're balanced. Let's start a company with USD 100.000 of cash:
| Movement Type | Debit Account | Credit Account | Debit # | Credit # |
|---|---|---|---|---|
| Ai | Cash | 100.000 | ||
| Ei | Equity | 100.000 |
Note
Ai stands for Asset increase.
Ei stands for Equity increase.
So you may notice that we increased Cash by 100.000. However the dumbfounding part is the Equity. Basically it is its counterpart, aka RAID1. The sum of all transactions in the debit column must be equal to the sum of all transactions of the Credit column. This is extremely similar to a CRC check.
Now let's say our company asks a $150.000 loan from the Bank:
| Movement Type | Deb Account | Credit Account | Debit # | Credit # |
|---|---|---|---|---|
| Ai | Cash | 150.000 | ||
| Li | Bank Loans | 150.000 |
Note
Li stands for Liability increase.
If we were to check our balance, it would look like this:
| Type | Deb Account | Cred Account | Debit | Credit |
|---|---|---|---|---|
| Asset | Cash | 250.000 | ||
| Liability | Bank Loans | 150.000 | ||
| Equity | Equity | 100.000 |
Notice that it's no longer "RAID1" in the sense that we do not store a duplicate of Cash, but rather it's more like RAID5 error codes because we must ensure that Bank Loans + Equity = Cash.
And now let's buy a car for $20.000 with our cash:
| Movement Type | Deb Account | Credit Account | Debit # | Credit # |
|---|---|---|---|---|
| Ai | Vehicles | 20.000 | ||
| Ad | Cash | 20.000 |
Note
Ad stands for Asset decrease.
If we see the balance it would now be:
| Type | Deb Account | Cred Account | Debit | Credit |
|---|---|---|---|---|
| Asset | Cash | 230.000 | ||
| Asset | Vehicles | 20.000 | ||
| Liability | Bank Loans | 150.000 | ||
| Equity | Equity | 100.000 |
Notice that 230.000 + 20.000 == 150.000 + 100.000.
It's also important to notice the following:
| Type | Sign of Transaction | Always goes in the column: |
|---|---|---|
| Asset | increase | Debit |
| Asset | decrease | Credit |
| Liability | increase | Credit |
| Liability | decrease | Debit |
| Equity | increase | Credit |
| Equity | decrease | Debit |
For example, if you see an Asset decrease on the Debit column, it's wrong. There are also other guarantees. For example the "Cash" account can never be negative (However some Asset accounts can be negative, such as Bank Accounts if they overdrafted, or Depreciation accounts. I won't go into Depreciation. Just be aware that as a general rule Assets being negative is like a code smell, but there are cases where it's allowed).
So, in the world of computers this may seem archaic and unnecessary. All a computer needs to do is:
json["Cash"] = 230.000;
json["Vehicles"] = 20.000;
json["Bank Loans"] = 150.000;
json["Equity"] = 100.000;And you don't really care about Double Entry or the type of account it is like Liability or EQuity. Real ECC RAM and RAID5 takes care of integrity and we call it a day!
Well, that's true in the sense of data integrity and when it comes to simple examples (and assuming your code doesn't have bugs like an integer underflow or a rounding error that gives you infinite money!).
But Double Entry helps a lot in complex situations where you're not sure how much you owe to the IRS.
For example:
- You sold $100k in merchandise.
- Your inventory stock at the beginning of the year is $150k
- Your inventory stock at the end of the year is $120k
- Your purchases throughout the year has been $30k
- How much do you owe the IRS? (let's assume a tax flat of 30%).
Let's analyze it with the Double Entry:
| Type | Deb Account | Cred Account | Debit | Credit |
|---|---|---|---|---|
| Ai | Cash | 150.000 | ||
| Ed | ??? | ??? | ||
| Ad | Inventory Stock | ??? | ||
| Ei | Sales | 150.000 |
We know we got 150.000 in cash. Thus our sales must have been 150.000. But what the hell about the rest?
So how much Inventory Stock did we sell? Ok let's see:
- Your inventory stock at the beginning of the year is $150k (we know this, because it was in our last year's report)
- Your inventory stock at the end of the year is $120k (we know this, thanks to an inventory audit we did on 30th December)
- Your purchases throughout the year has been $30k (we know this, thanks to our supplier's data or from their receipts)
- We don't know how many units we sold (the idiot who was supposed to track this and enter the sales on the system forgot it)
That means we must have sold:
what_we_had + purchases - costs = what_we_have_now
150k + 30k - costs = 120k
costs = 150k + 30k - 120k
costs = 60k
So we sold 60k in inventory stock. Whether some of our employees actually stole half of that, or it was thrown away because it got stale, or all of it was sold to customers, we'll never know. Because reality is imperfect and that registry was not logged (we may assume a fixed % to certain types of losses based on statistical averages from other data, but let's keep it simple).
So:
| Type | Deb Account | Cred Account | Debit | Credit |
|---|---|---|---|---|
| Ai | Cash | 150.000 | ||
| Ed | ??? | ??? | ||
| Ad | Inventory Stock | 60.000 | ||
| Ei | Sales | 150.000 |
We're almost there!
The other "???" account that is missing is the Equity account called "Cost of Merchandise". And by difference Double Entry, we know how much it is, and now it becomes obvious:
| Type | Deb Account | Cred Account | Debit | Credit |
|---|---|---|---|---|
| Ai | Cash | 150.000 | ||
| Ed | Cost of Merch | 60.000 | ||
| Ad | Inventory Stock | 60.000 | ||
| Ei | Sales | 150.000 |
Notice that the IRS is conceptually interested in the Equity accounts:
(Sales - Cost of Merchandise) x 30% = 90.000 x 30% = 27.000
So we owe $27.000 to the IRS.
But what was the point? We could have figured it out by doing:
Cash - Inventory Stock
Uh, no. Conceptually, that makes no sense. Why are you subtracting your inventory stock to a cash transaction? So if someone lends you money you're going to pay taxes to the IRS?! If you buy more stock that means you pay less taxes? It is very error prone.
Once complex international operations involving lots of purchases and sales and profits and losses; the Double Entry separates the flow of physical goods and services (like cash, bank transactions, and goods) from purely-abstract concepts like profits and losses.
Yes, profits and losses are abstract concepts because if you got $100 extra in your hand that doesn't mean you had $100 profit. Maybe someone lent it to you. But if you got it because you won the lottery, then it was indeed a profit.
Thus, the Double Entry is still very useful for conceptual reasoning and error checking wrong lines of thought.
Likewise if your code contains an error and now suddenly your system says you've got 4294967000 in cash, Double Entry acts as an extra check because the Debit & Credit columns will not match (they're unbalanced).