Polish, README and publishing to GitHub
The program works and is tested. This lesson is the difference between something on your machine and something you can show somebody — which is most of the value of having built it.
Read your own error messages
Go through every failure path as a first-time user would.
$ expenses add 100 groceries
Error: unknown category 'groceries'. Valid: food, transport, rent, other
Good — says what was wrong and what is acceptable.
$ expenses delete 999
Error: no expense with id 999
Fine, though better would tell them how to find a real one:
Error: no expense with id 999. Run 'expenses list' to see ids.
A good error message says what went wrong, and what to do next. The first part you have; the second is often one clause more and disproportionately useful.
Check that no traceback ever reaches the user. A traceback is for you.
Handle the empty cases
A new user's first run should not look broken:
$ expenses list
No expenses found.
$ expenses summary
No expenses recorded yet. Add one with: expenses add 250 food
Empty states are the first thing a new user sees and the last thing anybody tests. Telling them what to do next costs one line.
Check the output at real sizes
Run it with one expense, with fifty, and with an amount of ₹12,34,567.89.
Column alignment that looks fine with two rows often breaks with a large number. The f-string widths from module 2 are what you adjust here, and it is worth doing because misaligned columns read as carelessness.
$ expenses list
#1 2026-09-27 food ₹250.00 lunch
#2 2026-09-27 rent ₹15,000.00
#3 2026-10-03 transport ₹12,34,567.89 flight
Add --help text
argparse gives you --help, and it is only as good as your descriptions:
parser = argparse.ArgumentParser(
prog="expenses",
description="Track personal expenses from the command line.",
epilog="Data is stored in ~/.expenses.json",
)
add = sub.add_parser("add", help="record a new expense")
add.add_argument("amount", type=float, help="amount in rupees, e.g. 250.50")
add.add_argument("category", help="one of: food, transport, rent, other")
add.add_argument("--note", default="", help="optional description")
Run expenses --help and expenses add --help and read them as somebody who
has never seen the program. The epilog telling them where data lives answers a
question you would otherwise be asked.
Write the README
The most important file in the project, and the one most likely to be skipped.
# Expense Tracker
Track personal expenses from the command line. Data is stored as JSON in your
home directory — no database, no account, no network.
## Install
git clone https://github.com/you/expense-tracker.git
cd expense-tracker
python -m venv .venv
source .venv/bin/activate
python -m pip install -e .
## Usage
Record an expense:
expenses add 250 food --note "lunch"
List them, optionally filtered:
expenses list
expenses list --category food
expenses list --month 2026-09
See a breakdown:
expenses summary
Delete one by id:
expenses delete 1
Categories are food, transport, rent and other. Amounts are in rupees and
stored internally as paise, so totals are exact.
## Where the data lives
`~/.expenses.json` by default. Override with the `EXPENSES_FILE` environment
variable:
EXPENSES_FILE=/tmp/test.json expenses list
## Development
python -m pip install -e ".[dev]"
pytest
ruff check .
## Licence
MIT
What makes that useful:
It says what the program is in one sentence, including what it is not — no database, no account, no network. A reader knows within five seconds whether it is for them.
Every command can be copied and run. Not described — shown.
It explains a decision. The paise note answers "are the totals reliable?" before anybody asks.
It says where the data is. The single most common question about any command-line tool.
Write it before you forget which steps were not obvious. A README written months later always omits the thing that catches everyone.
Final checks
ruff format .
ruff check .
mypy src
pytest
All clean before you push.
Then the one people skip — clone it somewhere else and follow your own README exactly:
cd /tmp
git clone <your repo> check
cd check
python -m venv .venv && source .venv/bin/activate
python -m pip install -e .
expenses add 100 food
This catches the file you forgot to commit, the step you left out, and the dependency that only exists on your machine. It takes two minutes and is the only honest test of whether the project actually works for somebody else.
Push it
git add -A
git commit -m "Add README and improve error messages"
git remote add origin https://github.com/you/expense-tracker.git
git push -u origin main
Then look at it on GitHub as a stranger would. Check:
.venv/and.envare absent- The README renders correctly
- The repository has a description
- No secrets, no absolute paths from your machine, no leftover debug prints
What you have built
Worth stating plainly, because it is easy to undersell:
- A complete program that persists data between runs
- Validation that makes invalid data impossible to store
- Error handling that never shows a user a traceback
- Atomic writes, so a crash cannot corrupt the file
- Eighteen tests running in a tenth of a second
- A clean project that installs with one command
- A README somebody else can follow
That is not a toy. It is small, and everything in it is real — which is exactly what an employer or a client wants to see, far more than a half-finished clone of something ambitious.
Where to go next
Extend this. Budgets per category with a warning when exceeded. CSV export
using module 7. Recurring expenses. A --json flag for scripting. Each is a
small, complete addition, and each is easier because of the tests.
Then pick a direction. Web backends are the Django course. The interface in the browser is Web UI and JavaScript. Automation is largely this course applied to somebody's real problem — and it is the fastest way to be useful in a first job.
Build something for yourself. The single best thing you can do now is find a small problem you actually have and solve it. Motivation carries you through the difficult part in a way that exercises cannot, and it produces something you can show.
Check your work
Error messages that say what to do next.
Error: no expense with id 999
Error: no expense with id 999. Run 'expenses list' to see ids.
One clause more, and disproportionately useful. Check no traceback ever reaches a user — a traceback is for you.
Empty states.
$ expenses summary
No expenses recorded yet. Add one with: expenses add 250 food
The first thing a new user sees, and the last thing anybody tests.
Alignment at real sizes. With ₹12,34,567.89 in the list, a column width chosen for three-digit amounts breaks. Widen the f-string field rather than hoping — misaligned columns read as carelessness.
--help as a stranger. If your description says "add" and nothing else, it
is not help. The epilog telling them where data lives answers a question you
would otherwise be asked.
The clone-and-follow check is the one people skip and the only honest test of whether the project works for somebody else. It catches the file you forgot to commit, the step you left out of the README, and the dependency that exists only on your machine. Two minutes.
On GitHub as a stranger. No .venv, no .env, README rendering, a
description set, no absolute paths from your machine, no leftover debug prints.
What you have built. Worth stating plainly, because it is easy to undersell: a complete program with persistence, validation that makes invalid data impossible, error handling that never shows a traceback, atomic writes, tests that run in a tenth of a second, and a README somebody else can follow.
That is not a toy. It is small, and everything in it is real — which is considerably more use to an employer or a client than a half-finished clone of something ambitious.
Practice
- Go through every error path and improve any message that does not say what to do next.
- Add empty-state messages to
listandsummary. - Test with fifty expenses and a very large amount. Fix the alignment.
- Write help text for every command and argument. Read
--helpas a stranger. - Write the README. Include install, usage, data location and development.
- Run the four quality commands. Fix everything.
- Clone into a temporary folder and follow your own README exactly. Fix whatever fails.
- Push to GitHub and review it as a stranger.
- Add one extension of your choosing. Write its tests first.
That is the course
Eleven modules. You started by printing a line of text and finished with a tested, version-controlled program that somebody else can install and use.
Along the way you met the things tutorials usually skip: how to read an error, why a list changed when you did not touch it, why a default argument accumulates, what a traceback is telling you, when a class is the wrong answer, and how to leave code somebody else can pick up.
The practice was the course. If you skipped it, the reading alone will not have done much — and going back to do it now is time better spent than starting something new.
If you want mentorship alongside this, our internship programme is free, fully remote, and pairs this curriculum with real project work and code review from working developers.
Whatever you do next: build something small, finish it, and show somebody. That is the whole job.
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