The brief: an expense tracker
Ten modules of pieces. This one builds something whole.
The point of a capstone is not to learn new syntax — there is almost none here. It is that building a complete program is a different skill from completing exercises. Exercises tell you what to do. A brief tells you what is needed and leaves the deciding to you, and that gap is where most of the difficulty in real work lives.
The brief
Build a command-line expense tracker.
A user should be able to:
- Record an expense: amount, category, optional note, dated today
- List expenses, optionally filtered by category or month
- See a summary: total, per-category breakdown, monthly average
- Delete an expense by its id
- Have their data still there tomorrow
It must:
- Store data in a JSON file
- Reject invalid input with a message that explains the problem
- Not crash on a missing or corrupt data file
- Have tests for the logic
- Come with a README somebody else could follow
That is the whole brief, and it is deliberately written the way a real one arrives: what is needed, not how to build it.
What is not specified
Notice what the brief leaves open. These are yours to decide:
- Where the data file lives, and what shape the JSON takes
- What an id is
- The exact commands and their arguments
- How categories are validated
- How money is stored
- What happens when the file does not exist yet
Every one of those is a decision, and a few have right answers you already know. Money and floats was module 2. A missing file was module 6. A path that does not depend on where the program was run from was module 7.
Notice also what is not in scope: no web interface, no database, no multi-user, no currencies beyond rupees. A brief that says what is out of scope is a good brief, and asking "is this in scope?" is a reasonable question to put to whoever wrote it.
Why an expense tracker
Because it is small enough to finish and real enough to be awkward.
It has genuine validation rules, needs data that persists, involves dates, has several commands that share logic, and has an obvious way to be wrong — if the totals are incorrect, you notice.
It is also honestly useful. Plenty of people track expenses in a spreadsheet; this is a defensible thing to have built.
What finished looks like
$ expenses add 250 food --note "lunch"
Added #1: food ₹250.00 (lunch)
$ expenses add 15000 rent
Added #2: rent ₹15,000.00
$ expenses list
#1 2026-09-27 food ₹250.00 lunch
#2 2026-09-27 rent ₹15,000.00
$ expenses list --category food
#1 2026-09-27 food ₹250.00 lunch
$ expenses summary
Total: ₹15,250.00
rent ₹15,000.00 (98.4%)
food ₹250.00 (1.6%)
Across 1 month — average ₹15,250.00
$ expenses delete 1
Deleted #1: food ₹250.00 (lunch)
$ expenses add -50 food
Error: amount must be positive, got -50
$ expenses add 100 groceries
Error: unknown category 'groceries'. Valid: food, transport, rent, other
Worth noticing in those error messages: each says what was wrong and what is acceptable. That is the standard module 6 set for raising exceptions, applied to a user rather than a programmer.
What you already have
Nothing here needs anything you have not met:
| Need | Module |
|---|---|
| Storing money without float errors | 2 |
| Validating and converting input | 2, 6 |
| Filtering and grouping | 3, 4 |
| Functions and structure | 5 |
| Handling bad input and missing files | 6 |
| Reading and writing JSON, paths | 7 |
| Project layout, environment, dependencies | 8 |
An Expense type with rules |
9 |
| Tests, linting, Git | 10 |
If any row makes you uneasy, go back to that module now. Revisiting a lesson while building something is far more effective than reading it cold.
How to approach it
Four lessons follow: planning, building, testing, polishing. The order matters and it is worth stating why.
Plan first, on paper, before any code. Twenty minutes of thinking saves hours of restructuring — and the planning lesson is not padding.
Build in thin slices. Not all the storage, then all the commands. One command working end to end, then the next. You want something runnable within half an hour, because a program that runs is something you can check.
Write tests as you go, for the logic. Not at the end.
Polish last. Nice output on a program that does not work is wasted effort.
Two temptations to resist
Adding features. Budgets, charts, categories the user defines, export to Excel. All reasonable, all out of scope. Finish the brief, then extend it — unfinished projects are almost always ones whose scope grew while the core was still incomplete.
Making it perfect before it runs. Do not design a class hierarchy for a program that cannot yet add an expense. Get it working, then improve it. Module 5's refactor lesson was exactly this: working code first, structure second.
Check your work
No code yet, so these are decisions rather than answers — but several have a better and a worse option.
What an expense needs. An id, an amount, a category, a date, and an optional note. If you listed a currency, you have noticed the brief assumes rupees, which is a reasonable exclusion to write down.
Five ways a user gives you bad input. A non-numeric amount · zero or a negative amount · an unknown category · a note of unreasonable length · an id that does not exist for delete. Each needs a message that says what is acceptable, not only what was wrong.
The ambiguity. The brief does not say where the data file lives, what an id is, or what happens when the file is missing versus corrupt. The last is the one that matters — treating a corrupt file as empty means the next save destroys it.
What is not specified is the interesting part. A brief that leaves decisions open is normal; the skill is noticing them before you build rather than discovering them at step six.
The reference code for this capstone is at RizTech-Academy/python-foundation. Build it yourself first — it is there for comparing against when stuck, not for starting from.
Practice
Before the next lesson, write down — on paper, or in a text file:
- What data you need to store about a single expense.
- What the JSON file will look like. Write out an example with two expenses.
- What each command needs as input and what it prints.
- Three things that must always be true about an expense.
- Five ways a user could give you bad input, and what should happen for each.
- One thing in the brief you find ambiguous. That ambiguity is real — decide how to resolve it and write down your decision.
Do not write any code yet. Question 6 is the one that matters most: noticing ambiguity before building is the difference between a brief that goes smoothly and one that gets rebuilt.
Next: turning those notes into a plan.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship