Dev Essentials — 23 · Money Basics for Engineers — Budget, Emergency Fund, Compound Interest and Index Funds
Engineers learn exponential growth for algorithms, then often ignore it for their own money. Compound interest works for you in savings and against you in debt. Small fees grow into large sums over decades. This post teaches the basic ideas with formulas and small Python calculators you can run on your own numbers: budgeting, an emergency fund, debt, compound interest, inflation, index funds, fees, and the mistakes people make under stress.
Series map: 01 Text Encoding → … → 21 Estimating → 22 Design Docs → 23 Money Basics (this post) → 24 Desk Health. Part E · Growth outside code.
Watch out: this post is general education only, not personal financial advice. It does not recommend any product, fund, provider or asset. Account types, tax rules and pension schemes differ by country, so check your own country's regulator or government money-guidance service. For decisions about your own situation, talk to a qualified, regulated adviser.
Learning Outcome
- Track spending and test it against the 50/30/20 rule of thumb.
- Explain "pay yourself first" and size an emergency fund.
- Show with code how card interest makes a small debt last for years.
- Use and know how wrong the rule of 72 is.
- Turn a nominal return into a real (after-inflation) return.
- Explain stocks, bonds, index funds and diversification in plain words.
- Measure how a 1.0% fee compares with a 0.1% fee over 30 years.
- Describe what one Vanguard study found about lump sum versus dollar-cost averaging.
- Name the behaviour mistakes that cost investors the most.
Chapter 1 — Track spending, then budget
You cannot plan what you do not measure. Track every outflow for one full month. Most banks let you export a CSV. Put each line into one of three buckets:
- Needs — you would face real trouble if you did not pay: housing, basic food, transport to work, utilities, insurance, minimum debt payments.
- Wants — everything else you spend on.
- Savings — money moved to savings or investments, plus debt payments above the minimum.
The 50/30/20 split is a rule of thumb from Elizabeth Warren and Amelia Warren Tyagi's 2005 book All Your Worth. It says to aim for about 50% of after-tax income on needs, 30% on wants, 20% on savings. It is a starting point, not a law. In a high-rent city, needs may be well above 50%. That tells you where the pressure is; it does not mean you failed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""budget.py - sum a month of spending and compare with 50/30/20."""
import csv
import io
from collections import defaultdict
# Export from your bank or keep a sheet. bucket = need / want / save.
DATA = """date,description,amount,bucket
2026-09-01,rent,1500,need
2026-09-02,groceries,320,need
2026-09-03,transit pass,90,need
2026-09-05,phone + internet,85,need
2026-09-07,insurance,120,need
2026-09-10,restaurants,260,want
2026-09-12,streaming,25,want
2026-09-15,hobby gear,180,want
2026-09-20,travel fund,150,want
2026-09-25,transfer to savings,600,save
2026-09-25,extra debt payment,200,save
"""
TAKE_HOME = 4000 # monthly income after tax
TARGET = {"need": 0.50, "want": 0.30, "save": 0.20}
totals = defaultdict(float)
for row in csv.DictReader(io.StringIO(DATA)):
totals[row["bucket"]] += float(row["amount"])
unassigned = TAKE_HOME - sum(totals.values())
print(f"{'bucket':<6}{'spent':>9}{'share':>8}{'target':>8}")
for bucket, target in TARGET.items():
share = totals[bucket] / TAKE_HOME
flag = " <-- over" if bucket != "save" and share > target else ""
print(f"{bucket:<6}{totals[bucket]:>9.0f}{share:>8.0%}{target:>8.0%}{flag}")
print(f"left over: {unassigned:.0f}")
1
2
3
4
5
6
bucket spent share target
need 2115 53% 50% <-- over
want 615 15% 30%
save 800 20% 20%
left over: 470
To use your own data, replace io.StringIO(DATA) with open("export.csv", encoding="utf-8") and add a bucket column. The 470 "left over" is money with no job. Unplanned money tends to disappear, so give it one (often: more savings).
Watch out: these calculators use
floatfor teaching. For a real ledger, store money as integer cents orDecimal(see Dev Essentials 08 · Floating Point).
Chapter 2 — Pay yourself first
"Pay yourself first" means you move money to savings on payday, before you spend, instead of saving whatever is left at month end. Leftovers are usually close to zero.
The easiest way is an automatic transfer that runs the day your salary arrives. The US Consumer Financial Protection Bureau (CFPB) calls saving automatically "one of the easiest ways" to make saving consistent. It turns a daily choice into a one-time setup.
Trick: raise the automatic transfer each time your pay goes up. You never get used to spending the extra.
Chapter 3 — The emergency fund
An emergency fund is cash for surprises: job loss, a medical bill, a broken laptop. It stops a bad month from turning into high-interest debt.
- Size. US regulator FINRA says financial planners often recommend three to six months of living expenses. This is common guidance, not a rule. People with variable income, one income in the household, or a job that is hard to replace often aim higher.
- Where. Somewhere safe and easy to reach quickly, kept apart from spending money. It is not an investment; its job is to be there.
- Start small. If six months feels impossible, the CFPB suggests thinking about the unexpected costs you have actually had before and what they cost. A first target of one month is still a real safety net.
1
2
3
4
5
6
7
8
def runway_months(savings, monthly_essentials):
return savings / monthly_essentials
essentials = 2115 # the "need" total from Chapter 1
for months in (3, 6):
print(f"{months} months target: {months * essentials:,.0f}")
print(f"current runway: {runway_months(4500, essentials):.1f} months")
1
2
3
4
3 months target: 6,345
6 months target: 12,690
current runway: 2.1 months
Size the fund on needs, not total spending. In a real emergency, the wants stop.
Chapter 4 — High-interest debt: compounding against you
Credit card rates are high. The US Federal Reserve's G.19 release (September 2026) puts the average rate on card accounts that were charged interest at 22.15% APR for Q2 2026. Rates differ by country and by card; this number is only for scale.
APR is the yearly rate. A card with 22% APR charges roughly per month on the unpaid balance. On a 5,000 balance, that is about 92 of interest in the first month. If you pay 100, only about 8 reduces the debt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def payoff(balance, apr, payment):
"""Months and total interest to clear a card balance.
Simplification: interest = balance * apr / 12 each month (real cards
usually charge daily on the average daily balance), no new spending."""
monthly_rate = apr / 12
months, interest_paid = 0, 0.0
while balance > 0:
interest = balance * monthly_rate
if payment <= interest:
return None, None # the balance never falls
interest_paid += interest
balance = balance + interest - payment
months += 1
return months, interest_paid
for pay in (100, 150, 300, 500):
m, i = payoff(5000, 0.22, pay)
if m is None:
print(f"pay {pay}/month: never paid off")
else:
print(f"pay {pay}/month: {m} months, interest {i:,.0f}")
1
2
3
4
5
pay 100/month: 137 months, interest 8,678
pay 150/month: 52 months, interest 2,798
pay 300/month: 21 months, interest 1,022
pay 500/month: 12 months, interest 574
Paying 100 a month takes over 11 years and costs more in interest than the original debt. Paying 300 takes under 2 years. FINRA calls paying down high-rate debt, such as credit card debt, "one of the best ways" to improve your financial base. The reason is simple: clearing a 22% debt is like earning a sure 22% return, which almost no investment can promise.
Common pitfall: paying only the minimum. The minimum is designed to keep the account in good standing, not to clear the debt quickly.
Chapter 5 — Compound interest and the rule of 72
With compound interest, you earn interest on past interest. For a principal , yearly rate , compounding times a year, for years:
As grows without limit, this becomes continuous compounding, .
1
2
3
4
5
6
7
8
9
10
import math
def compound(P, r, n, t):
return P * (1 + r / n) ** (n * t)
P, r, t = 10_000, 0.06, 10
for label, n in [("yearly", 1), ("monthly", 12), ("daily", 365)]:
print(f"{label:<10} {compound(P, r, n, t):>10,.2f}")
print(f"{'continuous':<10} {P * math.exp(r * t):>10,.2f}")
1
2
3
4
5
yearly 17,908.48
monthly 18,193.97
daily 18,220.29
continuous 18,221.19
The compounding frequency matters a little. The rate and the time matter a lot.
The rule of 72 (an approximation)
To estimate how long money takes to double, divide 72 by the yearly rate in percent: at 6%, about years. The exact answer for yearly compounding is
1
2
3
4
5
for pct in (1, 2, 4, 6, 8, 10, 12, 18, 24):
approx = 72 / pct
exact = math.log(2) / math.log(1 + pct / 100)
print(f"{pct:>4}% {approx:>7.2f} {exact:>7.2f} {approx - exact:>+7.2f}")
| Rate | Rule of 72 (years) | Exact (years) | Error |
|---|---|---|---|
| 1% | 72.00 | 69.66 | +2.34 |
| 2% | 36.00 | 35.00 | +1.00 |
| 4% | 18.00 | 17.67 | +0.33 |
| 6% | 12.00 | 11.90 | +0.10 |
| 8% | 9.00 | 9.01 | −0.01 |
| 10% | 7.20 | 7.27 | −0.07 |
| 12% | 6.00 | 6.12 | −0.12 |
| 18% | 4.00 | 4.19 | −0.19 |
| 24% | 3.00 | 3.22 | −0.22 |
It is almost exact near 8%. At the edges of the table it is off by about 3% (at 1%) to 7% (at 24%) of the true answer. Why 72? For small , , which gives . Since is a bit less than , a slightly bigger number fits better for common rates, and 72 divides easily by 2, 3, 4, 6, 8, 9 and 12.
Trick: the rule works for debt too. A card balance at 24% with no payments doubles in about 3 years.
Chapter 6 — Inflation: real versus nominal
Inflation means prices rise, so each unit of money buys less. The nominal return is the number on your statement. The real return is what you gain in buying power:
where is the inflation rate.
1
2
3
4
5
6
7
def real_return(nominal, inflation):
return (1 + nominal) / (1 + inflation) - 1
print(f"{real_return(0.05, 0.03):.4%}") # 1.9417%
print(f"{1000 / 1.03 ** 10:.0f}") # 744
print(f"{1000 * (1.01 / 1.03) ** 10:.0f}") # 822
- A 5% return with 3% inflation is a real return of about 1.94%.
- With 3% inflation, 1,000 held as cash for 10 years buys what 744 buys today.
- Cash earning 1% during 3% inflation still loses buying power: 1,000 becomes 822 in today's money.
This is why long-term money is often invested rather than held as cash, and why an emergency fund (short-term money) is kept as cash anyway: for it, being available matters more than growth.
Chapter 7 — Stocks, bonds, index funds and diversification
Terminology
- Stock (share, equity) — a small piece of ownership in a company. Value goes up and down with the company and the market. Historically higher long-run returns, with large swings.
- Bond — a loan to a government or company, which pays interest and returns the loan amount at the end. Usually smaller swings than stocks; can still lose value, for example when interest rates rise or the borrower fails.
- Fund — a pool of many investors' money that buys many stocks and/or bonds.
- Mutual fund / ETF — two common fund structures. An ETF (exchange-traded fund) trades on an exchange during the day like a stock; a traditional mutual fund is bought and sold at one price per day.
- Index fund — the US SEC's investor site defines it as a mutual fund, ETF or unit investment trust that follows a passive strategy, aiming to match the return of a market index (such as the S&P 500) before fees.
- Expense ratio — a fund's yearly running cost as a percentage of your money in it.
- Asset allocation — how you split money between asset types (stocks, bonds, cash).
- Diversification — spreading money across many investments so that one failure does not sink you.
Why diversification helps
A single company can fail. A broad index holds hundreds or thousands of companies, so no single one decides your result. The SEC's beginners' guide explains that the right mix of investments may reduce losses and smooth returns without giving up too much potential gain. Diversification does not remove market risk: when the whole market falls, a diversified portfolio falls too, just less wildly than one bad stock.
Common pitfall: "diversifying" into many funds that hold the same companies. Five large-company funds from the same market may be one bet with five fees.
Chapter 8 — Risk versus time horizon
Time horizon is how long until you need the money. The SEC's guide says the right asset allocation depends largely on your time horizon and your ability to take risk.
A toy model shows why. It assumes each year's return is random with a mean of 6% and a standard deviation of 15%, years independent. These are made-up numbers, not a forecast. Real markets have crashes bigger than this model allows, and bad years can cluster.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"""horizon.py - a toy model of risk versus time horizon."""
import random
import statistics
random.seed(42)
TRIALS = 20_000
print(f"{'years':>5} {'worst 5%':>9} {'median':>7} {'best 5%':>8} {'P(loss)':>8}")
for years in (1, 5, 10, 20, 30):
yearly = []
losses = 0
for _ in range(TRIALS):
growth = 1.0
for _ in range(years):
growth *= 1 + random.gauss(0.06, 0.15)
yearly.append(growth ** (1 / years) - 1 if growth > 0 else -1.0)
losses += growth < 1
q = statistics.quantiles(yearly, n=20) # 5% steps
print(f"{years:>5} {q[0]:>9.1%} {statistics.median(yearly):>7.1%} "
f"{q[-1]:>8.1%} {losses / TRIALS:>8.1%}")
1
2
3
4
5
6
7
years worst 5% median best 5% P(loss)
1 -18.9% 5.9% 30.4% 34.7%
5 -6.1% 5.2% 16.3% 22.6%
10 -3.1% 5.0% 13.0% 15.1%
20 -0.7% 5.0% 10.6% 7.4%
30 0.3% 4.9% 9.5% 3.8%
In this model, the yearly average over a long period swings much less than a single year does, and the chance of ending below where you started falls. That is the logic behind a common guideline: money you need within a few years is kept out of volatile assets, while money for decades can ride out bad years. (Note also that the median is below 6%: big swings lower the typical compound result. This is often called volatility drag.)
Chapter 9 — Why fees matter
A fee is taken every year, from a balance that keeps growing. The loss compounds, just like returns do. The US SEC's investor bulletin on fees shows 100,000 invested for 20 years at 4% a year: with a 1% yearly fee, the result is nearly 30,000 lower than with a 0.25% fee.
Now a longer case. Assumptions (made up for illustration): start with 10,000, add 500 every month for 30 years, earn 6.0% a year before fees every year, fee charged monthly as a share of the balance, no taxes or inflation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"""fees.py - how a yearly fee changes an investment over 30 years."""
def grow(years, start, monthly_add, gross, fee):
balance, fees_paid = start, 0.0
for _ in range(years * 12):
balance *= 1 + gross / 12
fee_now = balance * fee / 12
balance -= fee_now
fees_paid += fee_now
balance += monthly_add
return balance, fees_paid
YEARS, START, ADD, GROSS = 30, 10_000, 500, 0.06
contributed = START + ADD * 12 * YEARS
print(f"money you put in: {contributed:,.0f}")
results = {}
for fee in (0.010, 0.001):
end, paid = grow(YEARS, START, ADD, GROSS, fee)
results[fee] = end
print(f"fee {fee:.1%}: end balance {end:,.0f} fees paid {paid:,.0f}")
gap = results[0.001] - results[0.010]
print(f"difference: {gap:,.0f} ({gap / results[0.001]:.1%} of the low-fee result)")
# Check against the SEC bulletin example: 100,000, 4% a year, 20 years
for fee in (0.0025, 0.0050, 0.0100):
print(f"SEC check, fee {fee:.2%}: {100_000 * (1 + 0.04 - fee) ** 20:,.0f}")
1
2
3
4
5
6
7
8
money you put in: 190,000
fee 1.0%: end balance 460,356 fees paid 54,396
fee 0.1%: end balance 551,156 fees paid 6,152
difference: 90,800 (16.5% of the low-fee result)
SEC check, fee 0.25%: 208,815
SEC check, fee 0.50%: 198,979
SEC check, fee 1.00%: 180,611
The gap (90,800) is much bigger than the extra fees paid (about 48,000). The rest is the growth those fees would have earned. The SEC check reproduces the bulletin: 0.50% costs about 10,000 and 1.00% costs about 28,000 compared with 0.25%.
Trick: when comparing funds that track the same index, the expense ratio is one of the few things you know for certain in advance. Returns are uncertain; fees are not.
Chapter 10 — Lump sum versus dollar-cost averaging
Suppose you have a large sum to invest at once (a bonus, an inheritance). You can invest it all now (lump sum) or feed it in over months (dollar-cost averaging, DCA).
One well-known study: Vanguard, "Dollar-cost averaging just means taking risk later" (Shtekhman, Tasopoulos and Wimmer, July 2012). Setup and findings:
- Markets: United States (1926–2011), United Kingdom (1976–2011), Australia (1984–2011).
- Portfolio: 60% stocks / 40% bonds. DCA: equal monthly amounts over 12 months, the rest held in cash. Results compared over rolling 10-year periods.
- Lump sum ended ahead in about two-thirds of periods: 67% (US), 67% (UK), 66% (Australia).
- On average the lump-sum portfolio ended 2.3% higher in the US (2.2% UK, 1.3% Australia).
- Longer DCA made it worse: in the US, lump sum beat 36-month DCA in about 90% of periods.
- DCA did better in falling markets. In US 12-month windows, lump sum lost money in 22.4% of periods and DCA in 17.6%.
The authors' reading: lump sum wins more often because stocks and bonds usually beat cash; DCA can make sense for someone mainly worried about short-term losses and regret. Note this is one study of past data; it does not predict any future period. Vanguard published a follow-up on the same question in February 2023.
Watch out: this study is about a sum you already have. Investing part of each paycheck as it arrives is also called DCA, but it is a different situation: the money did not exist earlier. The same paper calls that a prudent way to invest.
Chapter 11 — Behaviour: the costly mistakes
The biggest risk is often the investor, not the market.
- Chasing performance — buying what went up most recently, often near its peak.
- Panic selling — FINRA describes it as selling large amounts quickly out of fear of further falls, without careful analysis. It turns a paper loss into a real one and can miss the recovery.
- Overconfidence and over-trading — frequent changes add costs and timing mistakes.
- Checking too often — daily prices are mostly noise, and noise makes people act.
The damage is measurable. Morningstar's "Mind the Gap" 2025 study (one annual industry study) estimated that over the 10 years to December 2024, the average dollar in US mutual funds and ETFs earned about 7.0% a year, while the funds themselves returned about 8.2%. The 1.2-point gap, about 15% of the total return, came mostly from the timing of investors' buys and sells.
Defences that work because they remove in-the-moment decisions:
- Write a one-page plan: goal, time horizon, allocation, what you will do when markets fall 20%.
- Automate contributions (Chapter 2).
- Rebalance on a schedule (for example once a year), not on feelings.
- Rule of thumb: wait 24 hours before any unplanned change.
Practice Exercises
Exercise 1 — Your 50/30/20
Export one month of your own spending. Tag each line need / want / save and run budget.py on it.
Solution
Replace the DATA string with open("export.csv", encoding="utf-8") and add a bucket column in a spreadsheet first. If needs are well above 50%, look at the biggest single need (usually housing); small wants rarely fix a large needs gap. Use the split as a signal, not a grade.
Exercise 2 — Emergency fund target
Using your "need" total, compute the 3-month and 6-month targets and your current runway. How many months of automatic transfers would reach 3 months?
Solution
1
2
3
4
5
needs, saved, monthly_transfer = 2115, 4500, 600
target = 3 * needs
months = max(0, target - saved) / monthly_transfer
print(f"target {target:,.0f}, months to reach it: {months:.1f}") # 6,345 and 3.1
Exercise 3 — Rule of 72 versus exact
At what rate does the rule of 72 give exactly the right answer (to two decimals) for yearly compounding? Search rates from 7.0% to 9.0% in 0.01 steps.
Solution
1
2
3
4
5
import math
best = min((abs(72 / p - math.log(2) / math.log(1 + p / 100)), p)
for p in [x / 100 for x in range(700, 901)])
print(best) # smallest error is near 7.85%
The error crosses zero just under 8%, which is why the table shows almost no error at 8%.
Exercise 4 — Card payoff
A 3,000 balance at 24% APR. What monthly payment clears it in 24 months? Find it by trying values with payoff().
Solution
1
2
3
4
5
6
for pay in range(100, 300, 5):
m, i = payoff(3000, 0.24, pay)
if m is not None and m <= 24:
print(pay, m, round(i))
break
It prints 160 24 798: about 160 a month, costing about 798 in interest. The exact amortising payment is about 158.6.
Exercise 5 — Real return
A savings account pays 4% while inflation is 4.5%. What is the real return? Is money in that account growing?
Solution
. The balance grows in numbers but loses buying power. For an emergency fund that is an acceptable cost of safety; for 30-year money it is a slow leak.
Exercise 6 — Fees on your numbers
Change fees.py to your own start amount, monthly amount and years. Try fees of 0.05%, 0.2%, 0.5% and 1.5%.
Solution
Loop over (0.0005, 0.002, 0.005, 0.015). The pattern holds for any positive return: the gap grows faster than linearly with time, because each year's fee also removes that money's future growth. Keep the gross return the same for all fees so the comparison stays fair.
Cheat-sheet
| Topic | Key idea |
|---|---|
| 50/30/20 | Rule of thumb (Warren and Tyagi, 2005): needs / wants / savings of after-tax income |
| Pay yourself first | Automatic transfer on payday |
| Emergency fund | Common guidance: 3-6 months of living expenses, in cash, easy to reach |
| Card debt | APR / 12 per month; paying the minimum can take many years |
| Compound interest | ; continuous |
| Doubling time | Exact ; rule of 72 is an approximation, best near 8% |
| Real return | |
| Index fund | Passive fund aiming to match an index before fees |
| Diversification | Many holdings; lowers single-company risk, not market risk |
| Fees | Compound against you; 1.0% vs 0.1% over 30 years ≈ 16.5% less in the example |
| Lump sum vs DCA | Vanguard 2012: lump sum ahead ~2/3 of historical 10-year periods |
| Behaviour | Plan, automate, rebalance on a schedule, wait before acting |
What's next
Dev Essentials 24 · Desk Health closes the series with the body side of long hours at a computer: eye strain, posture, movement, sleep, and a checklist to measure your own setup.
References
- FINRA — Financial Foundations (emergency fund, high-interest debt)
- CFPB — An essential guide to building an emergency fund
- Simon & Schuster — All Your Worth (Warren and Tyagi, 2005)
- Federal Reserve G.19 Consumer Credit (credit card interest rates)
- SEC Investor Bulletin — How Fees and Expenses Affect Your Investment Portfolio (PDF)
- Investor.gov — Index Fund (glossary)
- Investor.gov — Beginners' Guide to Asset Allocation, Diversification, and Rebalancing
- Investor.gov — Compound Interest Calculator
- Vanguard (2012) — Dollar-cost averaging just means taking risk later (PDF copy)
- Vanguard (2023) — Cost averaging: Invest now or temporarily hold your cash? (PDF)
- FINRA — Key Terms for Tough Times (panic selling)
- Morningstar — Mind the Gap 2025 summary
Note: this post is in progress. Planned additions: a loan amortisation calculator, a net-worth tracker script, and a plain guide to reading a fund's fee and risk documents.










