Professional Documents
Culture Documents
for Developers
Accounting For Developers
In this ebook, we walk through basic accounting principles for anyone building
products that move and track money.
Introduction
As a payment operations startup, accounting principles are core to our work. We
have seen them implemented at scale at some of the largest fintechs and
marketplaces. Yet, accounting seems like an arcane topic when you’re starting
out. This HackerNews thread, for example, is rather representative of the state
of confusion around the topic.
Over the years, there have been many accounting for developer guides
published (two great ones are Martin Blais' and Martin Kleppmans’), but we
wanted to add our two cents to the discussion. In our experience, a
concepts-first approach to explaining accounting comes in handy when you are
designing systems that move or touch money.
In this ebook, we will cover the foundational accounting principles, then bring it
all together by walking through how to build a Venmo clone.
This guide is designed for developers that work on applications that handle
money in any way. You may work at a fintech company (the data you handle is
money), or perhaps you are responsible for managing the fintech integrations in
your startup (you handle data and money). We think that every engineer that
builds or maintains such systems benefits from knowing the core principles of
accounting.
CHAPTER 1
Accounts
An account is a segregated pool of value. The easiest analogy here is your own
bank checking account: money that a bank is holding on your behalf, clearly
demarcated as yours. Any discrete balance can be an account: from a user’s
balance on Venmo to the annual defense spending of the United States.
Accounts generally correlate with the balances you want to track.
Transactions
Transactions are atomic events that affect account balances. Transactions are
composed of entries. A transaction has at least two entries, each of which
corresponds to one account.
Let’s use a simple Venmo transfer as an example. Jim is sending $50 to Mary:
The entries in this transaction tell which accounts were affected. If each user’s
balance is set up as an account, a transaction can simultaneously write an entry
against each account.
Now, let’s expand this model with more accounts and additional events:
Here I have a ledger—a log of events with monetary impact. We often see
developers mutating balances directly rather than computing a balance from a
log of transactions. This is suboptimal.
Notice how each transaction has multiple entries. Each entry belongs to a
transaction and an account. By comparing entries side by side, one can
understand where the money came from and what it was used for.
This core idea—one transaction, at least two entries, one representing the
source and the other representing the use of funds—is one of the foundational
ideas of double-entry accounting. We’ll expand more on this later.
Dual Aspect
Let’s illustrate: the right side lists credit normal accounts and the left side lists
debit normal accounts. We’ll put accounts tracking uses of funds on the debit
normal side and accounts tracking sources of funds on the credit normal side.
Examples of uses of funds are assets and expenses. Buying inventory, making an
investment, acquiring physical property, and so on. The term “use” is broadly
defined: letting cash sit in a bank account is a use of funds, as well as selling on
credit to someone else (you are effectively “using” the money you’d get on a sale
by extending them credit). The accounts that represent these balances are all
debit normal accounts.
Part of the confusion is that “debits” and “credits” are often used as verbs: to
debit or to credit an account. Debits and credits can also refer to entries, for
example:
This sample transaction has two entries: we’re debiting our cash account and
crediting our equity account for $1M. Save for a few special situations,
accounting systems only log positive numbers. The effect on balances will
depend on whether the entry is on the “debit side“ or “credit side.”
Debits and credits are a shorthand for the expected effects on accounts,
depending on their type. A credit entry will always increase the balance of a
credit normal account and decrease the balance of a debit normal account. Put
differently:
Entries and their expected effects
Let’s model out a few transactions to drive this point home. Let’s use a fictitious
startup called Modern Bagelry—an eCommerce store for premium bagels.
In this example, we will use four accounts: Cash and Inventory (both debit
normal accounts) as well as Equity and Loans (both credit normal accounts).
Let’s say this ledger starts on a day T, and we are measuring time in days.
Let’s say we are aggregating the balances for each account in the example above
right after each transaction takes place:
Before moving on, let’s recap the principles we’ve reviewed so far:
If you’re curious about the API calls and system design considerations of
designing a digital wallet app, you can also check out our previous journal on
how to build a digital wallet.
To recap, for a system to gain the consistency benefits that accounting provides:
Let’s start with the product requirements of our Venmo clone, first from the
user’s perspective:
● Each user will have an account balance, exposed via the app
● Users can add to their balance by way of card payments
● Users can send money to each other in the app
● Users can withdraw balances into a bank account via ACH or a debit card
● Users will pay a small fee when they make a withdrawal from the app, to
be deducted from their wallet balance.
● We want to discern between the account balances for each user and
expose them to said users consistently;
● We want to ensure cash in hand in our bank account is always equal to the
total users deposited in the app;
● We want to properly calculate and collect revenue from fees;
● Each deposit will need to account for a 3% card transaction processing
fee paid by us.
With these requirements in mind, let’s map our chart of accounts (COA). The
COA is a simple depiction of the accounts we will need, their type, and
normality:
A cash account represents the amount of money we are holding in our bank
account in cash. Because it represents an asset or use of funds, we will treat it
as a debit normal account. For more information on debit and credit normal
accounts, refer to the “Dual Aspect” section of Part I of this series here.
The user balance accounts represent funds we are holding on behalf of our
users. Because users should be able to withdraw them at any time, they are
funds we ‘owe’—or liabilities. Those funds are technically now available for our
‘use’ - and as such, they are sources of funds. Therefore, they should be credit
normal accounts. Notice that we need one account for each customer that
creates an account with us.
To track card fees, we will be using a debit normal account. This account’s
balance will increase every time we pay off card processing fees. This is a debit
normal account because it represents expenses or uses of funds.
After mapping our chart of accounts, we should consider the typical events that
will affect the ledger. For the sake of this example, we will cover three
transaction types:
➊ Transfers
The user sends money from their balance to another user.
➋ Deposits
The user adds cash into their account balance. At the time of transfer, we
need to account for the credit card processing fee. (Let’s assume, for the
sake of this example, that credit card fees are paid by us.)
➌ Withdrawals
The user withdraws from their account balance. We charge a fee when
users withdraw from the app, deducted from their balance. At the time
of transfer, we need to account for our own service fee as revenue.
Let’s walk through the implementation for these transactions, starting with a
transfer:
This chart shows a typical transfer of $100 from Art to Brittany. In this case, the
transaction amount is debited (deducted) from Art’s Wallet (who’s initiating the
transfer) and credited (added) to Brittany’s Wallet (who’s the receiver).
Note that this logic can be used for any in-app transfer—we just have to
designate which wallet is the sender vs receiver in each case. All wallets are
represented as credit normal accounts. If Brittany was sending money to Art,
then Brittany’s balance would be debited (decrease), and Art’s balance would be
credited (increase).
In this model, three accounts are involved: the Art's Wallet, Cash, and Card
Processing Expenses. When Art deposits an amount into his wallet, he will see
the balance increase by the same amount. Simultaneously this will increase cash
balance and the total paid in processing fees.
To further illustrate this, let’s say Art deposits $300 in his wallet balance using a
credit card. Recall that for the sake of this example, our app is paying for card
fees. To counterbalance the $300 credit (increase) on Art’s Wallet, we need two
debit entries: one on the cash account (which increases it) and one on the card
processing expenses account (which also increases it).
For example, let’s say Brittany is withdrawing $500 from her wallet balance.
Brittany knows that she will pay a fee on that transfer amount. Let’s assume that
the fee is 0.5% of the withdrawal amount, or $2.50. Her user wallet gets
deducted for the entire $500 + $2.50, or $502.50. That is the debit entry
(decrease) on her user wallet balance.
To represent this on the credits side, we will add a credit entry that deducts the
cash account for $500, given this is actual money we wired out to Brittany.
However, we owe $2.50 less to Brittany and can recognize the fees we charged
from her as revenue by crediting (increasing) our revenue from fees account.
There are many different ways to model this. We could have chosen to have
Brittany receive $497.50 ($500-$2.50), for example. In this case, we would
add/credit the $2.50 we kept to revenue from fees similarly, but our cash would
only decrease/credit by $497.50. The ledger would still balance. Thinking in
terms of credit and debit normality gives you the flexibility to log transactions in
the best way for your business.
Let’s review the logical elements we would create to service this use case:
● One ledger object that represents the entire collection of accounts and
transactions. All of our accounts and transactions should belong to a
single ledger.
● At least four types of account objects:
○ User Wallets (one per user, credit normal)
○ Cash (single account, debit normal)
○ Revenue from Fees (single account, credit normal)
○ Card Processing Expenses (single account, debit normal)
● At least three modeled transactions
○ User Transfer
○ Deposit
○ Withdrawal
If you are building a ledger using a relational database you’ll want to model
accounts as belonging to a single ledger where transactions (or events) will be
written into. Accounts should have constraints according to their type: credit or
debit normal. Such constraints should dictate how debit or credit entries affect
account balances according to the principles we covered in our first two
chapters.
Similarly, transactions would have to be modeled in a way such that they are
composed of at least two entries. Such entries would have a ‘direction’, one of
debit or credit. Your system should enforce equality between the sum of
amounts on debit entries and the sum of amounts on credit entries.
The double-entry treatment for each type of transaction we covered on step 3
can then be mapped into functions in your application code that dictate how
you will write into the ledger database as transactions happen.
CHAPTER 4
Let’s start with the requirements for our fictitious Modern Lending. This is a
peer-to-peer lending marketplace where individuals can add money to a shared
pool of cash managed by Modern Lending. These investors can then set their
risk preferences, accept interest rates and invest directly on the platform.
Modern Lending then lends out the money to borrowers on the platform,
adjusting the amount and interest rate based on their creditworthiness.
Borrowers pay back their principal balance over time, and after repayments,
Modern Lending returns capital to investors, plus interest. Modern Lending
recognizes (books) the spread between the interest it gets paid by borrowers
and the interest it pays to investors as revenue.
Let’s assume there are no transaction costs or fees. We will also assume we are
building this application for scale—Modern Lending should be configured to
handle thousands of investors and borrowers triggering disbursements and
collections every day. Finally, let’s also assume we would like to build our ledger
flexibly to accommodate future product expansions.
Before we start with the ledgering setup, it’s important for us to understand
how data flows in a lending system. Modern Lending would need to implement
the following services infrastructure alongside its ledger.
How Modern Lending's systems come together
There are two kinds of data Modern Lending needs to keep track of:
● Historical data, or data that reliably represents the current financial state
of Modern Lending based on posted transactions.
● Prospective data, specifically those that are defined by the business
model: interest rates, loan terms, payment amounts, and the payment
breakdown of interest versus principal.
Modern Lending will need to make use of an amortization schedule, a tool that
utilizes interest rates, principal, and loan term to define monthly payments. It is
also common for lending businesses to front-load the percentage of a
customer’s payments that goes towards interest versus principal. An internal
service on Modern Lending’s backend can track this data and modify outputs
based on new inputs (i.e., new loan terms).
The ledger database acts in tandem with the amortization schedule. The ledger
acts as the source of truth for historical information. Every time the
amortization schedule service needs to provide an updated view of current
balances, it cross-references the amortization schedule with the ledger
database to provide accurate data.
Notice that the ledger also needs to interact with another service divorced from
the underlying ledger: payments and reconciliation logic. This service is our way
of representing Modern Lending’s payment processor of choice: this can be a
card processor or Modern Treasury Payments API if you’re using ACH, wire, or
RTP. Transactions come in as webhooks and get parsed as ledger transactions
according to the rules presented below.
You may have noticed we have a set of “n of” accounts. This is our way of
representing that each investor and each borrower will have two sets of
accounts: one tracking principal and one tracking interest. While we will only
need one cash and one revenue account, we need multiple sets of user
accounts.
Principal accounts track total capital invested by investors and total capital lent
to borrowers. The investor principal accounts are credit normal because they
represent sources of funds—or funds Modern Lending owes. Conversely,
borrower principal accounts are debit normal because they represent uses of
funds—akin to receivables.
Finally, interest accounts track the interest balance for both investors and
borrowers. They follow the same normality rules as the principal accounts: they
are credit normal when they track interest due to investors and debit normal
when they track interest that is owed to Modern Lending by the borrowers.
Let’s consider the typical set of events that will hit the ledger. As mentioned
before, a stream of transactions is parsed through the internal service handling
payments and reconciliation. Transactions are then written into the ledger
according to the rules presented below. The examples below are meant to be
illustrative of what transaction handling logic looks like - there are other
transaction types not presented here Modern Lending would need to parse out.
Here we cover the following archetypical transactions:
In this example, we’ll simplify our marketplace down to one investor (Brittany)
and one borrower (Art). We’ll also assume the following data about our loans is
coming from our amortization schedule:
Notice that we don’t record any kind of interest due to Brittany on the ledger at
this time, regardless of the fact Brittany was promised a 4.8% return upon
sign-up. Interest is ledgered in a separate transaction (see below).
Recall that Brittany lent $10,000 and expects a 4.8% return in a year. That means
her effective interest payment at maturity (when the loan is due) equates to
$480. Given we are assuming simple interest, we can see that we should add $40
to her interest balance to the ledger every month ($480/12). This is a credit
because we are increasing a credit normal account.
As outlined before, Art borrowed $5,000 at 12% to be paid in a year. This means
Art’s final interest balance will be $600 at the end of the year. Assuming simple
interest again, our ledger should add $50 every month to Art’s interest balance
($600/ 12). This is a debit because we are increasing a debit normal account.
(Let’s assume Brittany’s remaining $5,000 just sits on Modern Lending’s cash
account for now).
At the end of the year, Modern Lending will have received $600 from Art and will
owe Brittany $480 in interest. The difference of $120 is recognized as revenue.
Every month, as interest gets calculated, we add $10 to our revenue account
($120 / 12).
You may be wondering why we recognize revenue as interest gets calculated, as
opposed to when a borrower pays out their repayments or when an investor
receives their payout. There is a difference between booked revenue (accrual)
and realized revenue (cash). These two concepts are borne from accrual and
cash accounting principles.
In this case, we are using accrual accounting and booking revenue at the point
in which it is earned: when interest is incurred. In a marketplace of hundreds or
thousands, these transactions would be handled by a monthly cron job that
calculates interest and modifies balances on the ledger at a predetermined date.
Were we using cash accounting, we would add to the realized ledger only after a
borrower makes their repayments and investors get paid back. Because your
application ledger doesn’t need to comply with GAAP accounting rules, we
suggest you pick the method that leads to the cleanest ledger.
Every month Art makes a payment towards his balance. At the end of the term of
his loan Art will have to pay a total of $5,600, $5,000 in principal, and $600 in
interest. This translates into a monthly payment of $466.67.
After the transaction clears, we debit (increase) our cash account by $466.67. In
the same transaction, we credit (decrease) both principal and interest balances
for Art. Principal gets deducted by $416.67 ($5,000 / 12) and interest gets
deducted by $50 ($600 / 12).
At this point, the ledger shows one payment made by Art, a principal balance of
$4,583.33 ($5,000 - $416.67), and an interest balance of zero. This happens
because we recognized interest in the “interest calculation” section and then
immediately zeroed it out as the payment was made. The sum of credits towards
Art’s interest balance represents the total paid in interest. We can contrast this
with the data from Modern Lending’s amortization schedule to derive Art’s
remaining principal balance.
Finally, at the end of the year, Brittany would get disbursed her principal of
$10,000 in addition to the 4.8% return—or $480—promised to her. We credit
(decrease) our cash account, and simultaneously, we lower her principal and
interest down to zero by debiting (decreasing) $10,000 and $480, respectively.
Let’s review the elements we would need in place to service this use case:
The bottom line is that while using double-entry is the best way to ensure
integrity of the financial information flowing through Modern Lending’s
product, it requires a bit of setup and parsing through the most common
transactions. While this seems hard at first, understanding accounting
principles goes a long way. The hard part—we believe—is setting up the
underlying ledger database with the right constraints and level of flexibility.
In Summary
It can be onerous for generic databases to reliably handle double-entry
accounting. If you are a developer who works with money, the opportunity cost
of building a ledger from scratch may not be worth it. Modern Treasury Ledgers
simplifies the process of building a dependable double-entry system. Reach out
to us to learn more.