Double-Entry Bookkeeping
Every financial transaction in the system uses double-entry bookkeeping: each transaction debits one or more accounts and credits one or more accounts, with total debits always equaling total credits. This invariant — the accounting equation — ensures the ledger is always balanced:
Assets = Liabilities + Equity
No single-sided entries are permitted. Every journal entry is validated before persistence: SUM(debit_amounts) = SUM(credit_amounts) must hold exactly. If this check fails, the entry is rejected.
Chart of Accounts
The chart of accounts is a hierarchical taxonomy of all accounts in the organization:
accounts {
account_id UUID PK
org_id UUID FK
parent_account_id UUID FK nullable
name VARCHAR(255)
code VARCHAR(20) -- e.g. "1010"
type ENUM(ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE)
normal_balance ENUM(DEBIT, CREDIT)
is_leaf BOOLEAN
active BOOLEAN DEFAULT true
}
Example hierarchy: Assets (1000) → Current Assets (1010) → Cash (1011); Liabilities (2000) → Accounts Payable (2010). Normal balance is DEBIT for assets and expenses; CREDIT for liabilities, equity, and revenue. This determines how increases and decreases are recorded.
Journal Entry Schema
A journal entry records one complete transaction across two or more accounts:
journal_entries {
journal_id UUID PK
org_id UUID FK
description VARCHAR(500)
date DATE
reference VARCHAR(100) -- invoice number, receipt, etc.
created_by UUID FK users
created_at TIMESTAMP
}
journal_lines {
line_id UUID PK
journal_id UUID FK
account_id UUID FK
debit_amount DECIMAL(18,4) DEFAULT 0
credit_amount DECIMAL(18,4) DEFAULT 0
description VARCHAR(255)
}
A journal entry with two lines (one debit, one credit) is the simplest case. Complex transactions can span many lines, as long as the total debit equals total credit.
Journal Entry Immutability
Once posted, journal entries are never modified or deleted. This is a core accounting principle — altering historical records destroys the audit trail and violates accounting standards. Corrections use a two-step process:
- Reversing entry: create a new journal entry that exactly inverts the original (debits and credits swapped) to cancel its effect
- Correcting entry: create a new journal entry with the correct amounts
The original entry, the reversing entry, and the correcting entry all remain in the ledger, linked by a reversal_of reference field.
Account Balance Calculation
The current balance of an account is derived by summing all journal lines for that account:
-- For DEBIT-normal accounts (Assets, Expenses):
balance = SUM(debit_amount) - SUM(credit_amount)
-- For CREDIT-normal accounts (Liabilities, Equity, Revenue):
balance = SUM(credit_amount) - SUM(debit_amount)
For performance, a materialized account_balances table stores running totals, updated transactionally on each journal entry insert. Point-in-time balances are computed by summing lines up to a given date.
Period Closing
At the end of each accounting period (month, quarter, year):
- Calculate net income: SUM(revenue account balances) – SUM(expense account balances)
- Post a closing journal entry: debit all revenue accounts to zero, credit all expense accounts to zero, with the net income flowing to Retained Earnings (equity account)
- Mark the period as CLOSED in the
accounting_periodstable - Reject any new journal entries dated within a closed period
After closing, revenue and expense account balances reset to zero for the new period, while asset, liability, and equity balances carry forward.
Balance Sheet
The balance sheet is a point-in-time report showing financial position. It aggregates account balances as of a specific date:
- Assets section: sum of all ASSET account balances grouped by sub-category
- Liabilities section: sum of all LIABILITY account balances
- Equity section: sum of all EQUITY account balances including retained earnings
- Validation: Total Assets must equal Total Liabilities + Total Equity
Profit and Loss Report
The P&L (income statement) covers a date range and shows:
- Revenue: sum of all REVENUE account activity for the period
- Expenses: sum of all EXPENSE account activity, broken down by category
- Net Income: Revenue – Expenses
Both reports support drill-down: clicking a line item shows the individual journal entries that compose it.
Multi-Currency Support
All amounts are stored in the organization's functional currency. Foreign currency transactions are recorded with the exchange rate at transaction date:
journal_lines + { foreign_currency CHAR(3), foreign_amount DECIMAL(18,4), exchange_rate DECIMAL(12,6) }
At period end, a revaluation run recalculates the functional currency value of foreign-denominated balances at the current exchange rate. The difference is posted as an unrealized FX gain or loss to a dedicated equity account.
Audit Trail
Every journal entry is immutable and carries the creator's user_id and creation timestamp. No UPDATE or DELETE is permitted on journal_entries or journal_lines — enforced at the database level via trigger or application-level constraint. The full history of every transaction is always recoverable, making the ledger suitable for external audit and regulatory compliance.
See also: Stripe Interview Guide 2026: Process, Bug Bash Round, and Payment Systems
See also: Coinbase Interview Guide
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering