Python Capstone: Build an Expense Tracker
Combine validated dataclasses, collections, JSON persistence, reports, type hints, tests, and a command-line interface in a finished expense tracker.
Before this lesson
Design a layered Python application
Persist versioned validated data
Ship tests and reproducible setup instructions
The short answer
Build the capstone in layers: a validated Expense model, pure reporting functions, a JSON repository, and a thin command-line interface. Test each layer independently, then verify the complete add-list-summary-save-reload workflow.
Build the mental model
The expense tracker has four boundaries: user commands, domain rules, storage, and presentation. Keeping them separate prevents input calls and file paths from leaking into every function. The domain layer should work entirely in memory.
The central idea in this lesson is integrated application design. Read the rule, predict a concrete outcome, and then run the smallest example that can confirm or reject the prediction. This separates knowledge from familiarity with syntax.
Make the design choice explicit
Represent each expense with an identifier, date, category, description, and positive amount. Store a schema version beside records. Make the repository translate between JSON values and domain objects while reports operate only on validated objects.
from dataclasses import dataclass
@dataclass(frozen=True)
class Expense:
category: str
amount: float
def totals_by_category(expenses):
totals = {}
for expense in expenses:
totals[expense.category] = totals.get(expense.category, 0) + expense.amount
return totals
expenses = [Expense("food", 12.5), Expense("travel", 8), Expense("food", 4.5)]
print(totals_by_category(expenses))Expected output
{'food': 17.0, 'travel': 8}Trace the example before running it. Identify each input, transformation, returned value, and side effect. Then change one boundary value and explain why the new behavior follows from the rule rather than memorizing the output.
Recognize failure modes
Do not begin with a giant menu loop. First prove model creation, totals by category, save and reload, then connect commands. Write a temporary file before replacement and show actionable errors without erasing the last valid data.
Use a professional practice loop
Turn the concept into a repeatable workflow: write down the expected behavior, implement one coherent change, run a representative example, and retain a regression check. If the result surprises you, capture the exact input and error before changing anything.
Review the program for names, boundaries, and hidden side effects. A solution is complete when another developer can understand its contract, reproduce its setup, and verify both the successful path and one meaningful failure path.
Quick knowledge check
Answer before you reveal.
01Why keep reporting functions independent of files and input()?
Pure reporting functions are easier to test, reuse, and reason about; adapters can handle storage and interaction separately.
02What four steps make the practice loop reliable?
Predict the behavior, make one coherent change, run it, and retain a check that detects regression.
Exercise
Practice challenge
Build the complete expense tracker with add, list, category summary, versioned JSON persistence, validation, type hints, and automated tests.
Requirements
- The successful path produces the documented result
- At least one boundary or invalid case is handled deliberately
- Calculation or domain logic is separated from console interaction
Optional extension: Add one automated regression check for the most important rule.
Open in Python compilerLesson checkpoint
One small step locks it in
Mark this lesson complete, then keep the momentum going.
Clear up the details
Frequently asked questions
Is integrated application design only important in large programs?
No. Small programs reveal the same rules with less noise, and learning the rule early prevents fragile habits from becoming architecture.
Should I memorize every API used here?
No. Memorize the mental model and how to verify behavior. Use documentation for exact names and parameters when needed.
How do I know the exercise is finished?
Meet every success criterion, test at least one boundary or failure case, and explain why the output follows from the code.