Your project works, it is tested and it lives in a tidy repository: Implementation and testing closed off the "done" checklist. There is one part left that beginners usually treat as an afterthought and that in practice decides how much your work is worth to other people: knowing how to show it. A project nobody can install, that makes no sense without you standing next to it, or whose decisions you cannot justify is, for all practical purposes, a project that does not exist. This lesson turns your code folder into something presentable: a clean repository, a README that does the work for you, a five-minute demo that does not depend on luck, and prepared answers to the questions you are going to be asked — including the most uncomfortable one, about the limitations.
Contents
- Making the repository presentable
- The README as a covering letter
- The five-minute demo
- Explaining technical decisions
- Talking about limitations without selling yourself short
- Typical questions and how to answer them
- Taking criticism and turning it into tasks
- Publishing the project
- Final self-assessment and improvement plan
- Common mistakes and tips
- Exercises
- Conclusion
- Making the repository presentable
Whoever looks at your project will see the repository first, not the program. And the first impression is decided in thirty seconds: if there is a README.md explaining what this is and the files have sensible names, they carry on reading; if they see a folder with test2.py, data_final_final.json and no text at all, they close the tab.
Go through this whole checklist before showing it to anybody, picking up Documentation and comments and Version control:
| # | Check | Why it matters |
|---|---|---|
| 1 | Complete README.md, with installation, usage and a sample session |
It is the only thing many people will read |
| 2 | requirements.txt (or requirements-dev.txt) with the dependencies |
Without it nobody reproduces your environment |
| 3 | .gitignore with .venv/, __pycache__/, *.log and your real data |
A repository full of junk costs credibility |
| 4 | No personal data or credentials in any file or in the history | It is the most serious mistake you can make |
| 5 | No temporary files, test.py, backup/ or commented-out code |
Everything surplus distracts from what matters |
| 6 | Consistent file and branch names, in a single language | Consistency reads as professionalism |
| 7 | Readable git log --oneline, with messages starting with a verb |
It tells the project's story |
| 8 | Clean git status, everything committed and pushed |
What is not pushed does not exist |
| 9 | v1.0 tag created and pushed |
It marks the deliverable point |
| 10 | Cloned into a new folder: installs and starts | The definitive check |
A serious warning about point 4: deleting a file containing a password in a later commit does not remove it from the history; it is still there and anybody can recover it. If it has happened to you, the right response is not to paper over it but to invalidate that credential and, if the repository is not public yet, redo it from scratch. In a project from this course you normally will not have credentials, but you will have real personal data: your own, in the expenses file or in the contact book. That does not get pushed.
And to close the work off, picking up the semantic versioning from 08-01:
- The README as a covering letter
The README is the most profitable document in your project: it takes an hour to write and it is what 100 % of the people who look at your project will read. This is the structure, in order, with the reason for each section:
| Section | What goes in it | Length |
|---|---|---|
| Title and one-liner | What this is, in one line | 1 line |
| Description | The problem it solves and for whom | 3-5 lines |
| Features | A list of what it does | 5-8 points |
| Requirements | Python version and dependencies | 2 lines |
| Installation | Copy-pastable commands, in order | 4-6 lines |
| Usage | How to run it and a real sample session | 15-25 lines |
| Project structure | File tree with one line per module | 8-10 lines |
| Tests | How to run them | 2 lines |
| Limitations and future improvements | What it does not do, on purpose | 4-6 points |
| Licence and authorship | Who made it | 1-2 lines |
An annotated extract from the MyExpenses README:
# MyExpenses
Personal expense tracker for the command line.
Records income and expenses with a category and a date, saves them in a JSON file
and shows the monthly breakdown by category. It came out of a problem of my own:
getting to the end of the month with no idea where the money had gone.
## Features
- Recording expenses and income with validation of every field
- Listing a month's transactions, sorted by date
- Summary by category with percentages and the month's balance
- Exporting the month to CSV for the spreadsheet
- Automatic saving to JSON; tolerates a missing or corrupt file
## Requirements
Python 3.10 or later. No external dependencies.
## Installation
(a bash code block goes here with the commands, one per line)
## Usage
(another code block with: python -m myexpenses)The last two sections carry code blocks with the literal commands, which are these:
git clone https://github.com/username/myexpenses.git
cd myexpenses
python -m venv .venv && source .venv/bin/activate
python -m myexpensesNotice three decisions. The description mentions the problem before the solution: the reader understands what it is for before knowing how it works. The features are written as nouns or infinitives, not as "you will be able to...". And the installation is copy-pastable commands in order, with no prose in between: whoever is assessing your project wants to paste them and have them work.
The part that convinces the most, and the one almost nobody includes, is a text demo of a real session. It is worth more than any description, because it shows the program working without installing it:
$ python -m myexpenses
=== MyExpenses ===
1. Record expense
4. Summary by category
0. Exit
Option: 4
Month (YYYY-MM): 2026-08
=========================================
SUMMARY FOR 2026-08 (18)
=========================================
food -212.40 EUR 38.1 %
transport -132.00 EUR 23.7 %
home -98.50 EUR 17.7 %
-----------------------------------------
Expenses -557.10 EUR
Income +1450.00 EUR
BALANCE +892.90 EUR
=========================================How it is done: run the program with your sample data, copy the literal output from the terminal and paste it into a ```text block. Do not recreate it by hand — it shows, and it usually ends up misaligned. If you prefer screenshots, upload them to a docs/ folder in the repository and link them with ; but text has an advantage: it can be searched, copied and does not break.
- The five-minute demo
Sooner or later you will have to show the project live: in class, in an interview or to somebody who asked what you were working on. Five minutes sounds like very little and is a great deal if you have no script, because the natural impulse is to start with the code and get lost in the details.
flowchart LR
P["Problem<br/>30 s"] --> S["Solution<br/>30 s"]
S --> D["Flow demo<br/>2 min"]
D --> T["Technical detail<br/>1 min"]
T --> L["Limitations and<br/>next steps 1 min"]
This is the script, and how the time is shared out matters as much as the content:
| Block | Time | What you say | Typical mistake |
|---|---|---|---|
| 1. The problem | 30 s | "I used to jot expenses down in phone notes and by the end of the month I had no idea where my money went" | Starting with "I used dataclasses and JSON" |
| 2. The solution | 30 s | What the program is in one sentence and what it does | Listing all ten functions |
| 3. Demo of the main flow | 2 min | Record an expense and see the month's summary, with data already loaded | Improvising and typing data by hand |
| 4. One technical detail | 1 min | Something you are proud of, with the reason for it | Showing code without explaining the decision |
| 5. Limitations and next steps | 1 min | What you deliberately left out and what you would do next | Apologising for what is missing |
About block 3, which is the one that can go wrong: prepare the data in advance. Have a demo_data.json with fifteen or twenty transactions spread over two months, copied into place just before you start. Never do a demo with an empty database: recording five expenses live eats your two minutes and shows nothing. And rehearse the exact key sequence at least twice; the aim is to be able to talk while you type it, not to read off the screen.
And what if something fails during the demo? It will, sooner or later. What is being judged is not the failure, it is the reaction:
- Do not hide it or play it down. "There is a bug there, I had not tested this combination" is a professional response. Pretending it did not happen is not.
- Do not start debugging live. It eats everybody's time and rarely turns out well. Note it down and carry on.
- Have a plan B: a screenshot or a text-recorded session in the README you can carry on explaining from.
- Turn the failure into information. "That is a case that is not in my test script; I am adding it" shows that you have a method, which is exactly what is being assessed.
- Explaining technical decisions
Everything in your code is the result of a decision, and the question "why did you do it that way?" is not an attack: it is the standard way of finding out whether you understand what you have done. A good answer has three parts: what you chose, what alternative you ruled out and on what criterion. Three real examples about MyExpenses:
"Why a
Transactionclass and not a dictionary?" I started with dictionaries, which is the quickest thing. The problem showed up with validation: every place that created a transaction had to check that the amount was not zero and that the category existed, and I forgot in one of them. With a class, the validation is in__post_init__and an invalid transaction cannot exist in the program. On top of that,__str__gives me the table row ready formatted. If it were only a data container with no rules, I would have kept the dictionary.
"Why JSON and not CSV, given that these are flat records?" Because of the types. In CSV everything comes back as a string and I would have to reconvert amounts and dates when reading, with the risk of failing silently. JSON preserves the numbers and lets me add optional fields without breaking the old files, which is also why I store a version number. I do use CSV, but only as a one-way export, which is where it has the advantage: opening the month in the spreadsheet.
"Why did you separate the interface from the logic, if it is a small program?" Mostly so I could test it. The summary calculations are in
Ledger, which prints nothing, so I can verify them withpytestin hundredths of a second; if they were mixed in with the
The pattern shows in all three: context, alternative, criterion, consequence. And something just as important: if you took a decision without thinking, say so. "I did it that way because it was what I knew how to do, and now that I look at it, it would have been better to..." is an answer that adds value, because it shows judgement you have acquired since. The answer that costs you is inventing a justification you cannot sustain under a follow-up question.
- Talking about limitations without selling yourself short
Every project has gaps, and yours, being the first, has quite a few. The difference between looking like a beginner who does not know and a professional who is starting out lies entirely in how they are told. The pattern has three beats:
"I know → I left it out on purpose → this is how I would do it"
Applied:
| Limitation | Version that costs you | Version with the pattern |
|---|---|---|
| No graphical interface | "I did not have time to do the interface" | "It is command-line on purpose: I wanted to focus the effort on the data model and the tests. The interface is isolated in interface.py, so putting a web front end on top would be a matter of replacing that layer" |
| No multi-user support | "It only works for one person" | "It is single-user by design: it solved my problem. For several you would need a user identifier in the model and one file per user, or a database outright" |
| Everything in memory | "If it had a lot of data it might get slow" | "I load everything into memory at startup, which with a few thousand transactions responds instantly. Beyond tens of thousands you would have to move to SQLite; I measured it before deciding (06-04)" |
The three versions on the right share something: they turn a gap into a decision and propose the way forward. That is only possible if you genuinely took the decision, and that is why the Won't box from Project definition was so important: it is now your script.
Two things you must never do: apologise on a loop ("sorry, I know it is badly done, I am just a beginner"), which invites people to look at the project with suspicion and adds no information; and inventing limitations you do not have to seem humble. Say what it is.
- Typical questions and how to answer them
These questions come up almost every time. Prepare them in writing; answering a predictable question well is one of the things that most distinguishes one presentation from another:
| Question | What they want to know | How to angle the answer |
|---|---|---|
| What did you find hardest? | Whether you can recognise difficulties and how you solve them | A specific problem and the method you solved it with, not "it was all hard" |
| What would you do differently if you started today? | Judgement acquired | One or two concrete decisions with their reasons |
| How do you know it works? | Whether you test or just trust | The automated tests, what they cover, and the interface's manual script |
| What happens if the user types anything at all? | Robustness | Show it live: type something absurd and show the error message |
| How long would it take you to add X? | Whether you know your own code | Name the files you would touch and give an honest range |
| Why this file structure? | Whether you understand the layers or copied them | One sentence per module and the rule of downward dependencies |
| Have you used AI? | Honesty and judgement | Tell the truth and explain what for: understanding errors, reviewing, learning |
| Would it scale to a lot of data? | Whether you think about costs | The big-O of your main operations and the volume at which you would change |
| What have you learned? | Self-awareness | Be specific: "to split the work into increments", not "I have learned Python" |
On the AI one: the honest answer is always better. "I used it to understand a TypeError I could not read and to have it review my storage.py; the design and the code are mine" is a professional and verifiable answer — because if the code is yours, you will be able to explain it.
- Taking criticism and turning it into tasks
When you show your project you get comments, and the instinctive reaction is to defend yourself. That is a mistake: criticism from somebody who has taken the trouble to look at your code is one of the most valuable things you will ever get for free. The procedure:
- Listen to the whole observation without interrupting and without justifying yourself yet.
- Write it down verbatim. Do not interpret it in the moment; you can interpret it later in the cold light of day.
- Ask if you do not understand it. "What do you mean by the module doing too many things?" is a legitimate question and shows interest.
- Say thank you. Even if you disagree. Listening costs nothing.
- Later, in the cold light of day, classify it into one of these four boxes.
| Type of comment | Example | What you do |
|---|---|---|
| Real bug | "It blows up with the month 2026-13" |
Immediate task: reproduce, fix, regression test |
| Sound improvement | "The percentages should be rounded to the same decimal" | A task for v1.1 |
| Personal preference | "I would have done it with list comprehensions" | Listen to it, weigh it up, you decide |
| Out of scope | "It needs a mobile app" | Into the Won't box with its justification |
The fourth box is where you suffer most if you are not clear about it: there are comments that describe a different project, not yours. And the first is the most valuable of all: somebody who finds a bug in your program is doing you a favour, even if it does not feel like it at the time. Turn every comment from the first two boxes into a line on your task list, with its priority; that is where the improvement plan in section 9 comes from.
- Publishing the project
A project on your hard drive is no use to anybody, starting with you. Publishing it takes twenty minutes:
# With the repository already created on GitHub, empty:
git remote add origin https://github.com/username/myexpenses.git
git branch -M main
git push -u origin main --tagsThen spend a while on the repository page, which is your shop window:
- Short description (the About field): one sentence with what it does and what it is written in. "Personal command-line expense tracker in Python, with JSON persistence and pytest tests".
- Topics:
python,cli,json,pytest,learning-project. They help it turn up in searches. - A well-rendered README: look at it on GitHub, not just in the editor. Check that the tables show up and that the code blocks have their language.
- Licence: if you want others to use it, add an MIT one; without a licence, technically nobody can reuse it.
On the portfolio: two or three finished projects, explained and with a commit history, are worth more than twenty half-built repositories. Whoever assesses you looks at whether the project is finished, whether the README explains the why, whether there are tests and whether the commits tell a coherent story. A small, complete project communicates "this person finishes things", which is exactly the signal being looked for.
And a practice that teaches more than it looks: looking at other people's projects. Search GitHub for a Python command-line application with few stars — huge projects are unreadable at first — and examine, in this order: the README (do you understand what it does in one minute?), the file structure (do you recognise the layers?), any module (how do they name their functions?), the tests (what do they test?) and the history (how do they write their messages?). Half an hour doing this gives you reference points no tutorial can.
- Final self-assessment and improvement plan
The moment has come to use the rubric you wrote in 09-01, back when you still did not know how the project would turn out. Fill it in honestly — the only person you would be fooling is yourself — and multiply each score by its weight:
| Criterion | Weight | Your score (1-4) | Concrete evidence that justifies it |
|---|---|---|---|
| Functionality | 25 % | Which Must requirements work? Any half-done? | |
| Code structure | 20 % | How many modules and with what responsibility? | |
| Robustness | 15 % | What odd inputs did you test and what happened? | |
| Documentation | 15 % | README, docstrings, annotations, CHANGELOG? | |
| Tests | 15 % | How many and covering which layers? | |
| Use of Git | 10 % | How many commits, with what messages, is there a tag? |
The right-hand column is what makes the exercise useful: it forces you to justify the score with evidence, not with an impression. A 3 in tests means being able to say "eleven tests covering the model, the aggregation and the save/load cycle", not "I think they are fine".
With the rubric filled in, the improvement plan writes itself: sort the lowest-scoring criteria by descending weight. Here is MyExpenses's, which came out with an average of 2.85:
| Priority | Criterion | Score | Concrete action | Cost |
|---|---|---|---|---|
| 1 | Robustness (15 %) | 2 | Validate the month format and catch PermissionError when saving |
1 h |
| 2 | Tests (15 %) | 2 | Add tests for for_month with an empty month and for the CSV export |
2 h |
| 3 | Documentation (15 %) | 3 | Add a CHANGELOG.md and docstrings to interface.py |
1 h |
| 4 | Functionality (25 %) | 3 | Implement "edit transaction", which was left in Should | 3 h |
Seven hours of work that raise the average to 3.4. And notice the order: first the cheap thing that fixes a low score, then the expensive one. Adding a new feature is the most fun and is almost never what improves the project most.
Common Mistakes and Tips
- Showing the code before the problem. Nobody can judge a solution without knowing what problem it solves. Thirty seconds of context first, always.
- A three-line README. "Final project of the Python course" says nothing. If you only have one hour to polish the project, spend the whole hour here: it is what multiplies the most.
- A demo with no prepared data. Entering records live eats the time and bores people. Have a demo file ready and rehearsed.
- Apologising all the time. It lowers the value of what you have done and adds no information. Use the "I know, it was on purpose, this is how I would do it" pattern.
- Defending yourself against criticism. Listening is free and deciding is yours. Note it down, say thank you, classify later in the cold light of day.
- Uploading personal data. Review the repository before making it public: the file with your real data does not get pushed, and deleting it afterwards does not remove it from the history.
- Tip: tell somebody who does not program about the project out loud. If you can get them to understand what it does and why, your presentation is sorted.
- Tip: write your five-minute script and time it. It is almost always eight the first time, and what is surplus is the technical block.
Exercises
The last real steps in your project. Once you finish them, it is delivered.
Exercise 1: Repository and README
Go through the checklist in section 1 point by point and fix whatever fails, including the check of cloning into a new folder. Then write your complete README.md with the ten sections from section 2, including a text demo of a real session copied literally from your terminal. Create the v1.0 tag and push it.
Exercise 2: Demo script and technical decisions
Write your five-minute script following the table in section 3, with the time allocated to each block and the demo data prepared in a separate file. Rehearse it against the clock twice. Then write out three technical decisions from your project using the context-alternative-criterion-consequence pattern, and three limitations using the "I know, I left it out on purpose, this is how I would do it" pattern.
Exercise 3: Self-assessment and improvement plan
Fill in the rubric from section 9 with concrete evidence for each criterion and work out your weighted score. Then build the improvement plan by sorting the lowest-scoring criteria by weight, with a concrete action and an estimate per row. Finally, pick the priority 1 action and do it; then score that criterion again.
Solutions
Solution 1. Section 2 contains the structure and the extract from the MyExpenses README. The three most common failings: a README that describes the code instead of the problem, an installation section with prose between the commands, and no session demo. Self-assessment rubric: would somebody who does not know your project understand in one minute what it does and for whom? Can they install it by copying and pasting, without asking you anything? Is there a real sample session? Does the limitations section exist and is it written as decisions? Does git tag show v1.0?
Solution 2. Section 3 has the script with timings and section 4 has the three examples of a well-argued decision — class versus dictionary, JSON versus CSV, separating the layers — with the structure context, alternative, criterion and consequence. Section 5 does the same for the limitations. Rubric: does your script spend the first 60 seconds on the problem and the solution, without mentioning technology? Is the demo of a real flow with data already loaded? Does each decision mention the alternative you ruled out? Does each limitation end by proposing how it would be solved? Does it fit into five timed minutes?
Solution 3. Section 9 has the rubric and the improvement plan for MyExpenses, with its score of 2.85 and the four actions sorted by weight. What usually goes wrong in this exercise is generosity: if you give yourself a 4 in tests with three scattered tests, the rubric stops being any use at all. Rubric for the rubric: does each score have concrete, numeric evidence next to it? Is your plan sorted by the criterion's weight, not by what you feel like doing? Does each action fit into three hours? Have you already done the first one?
Conclusion
A project you cannot explain does not count, and explaining it starts with the repository: a complete README, requirements.txt, .gitignore, zero personal data or credentials, no temporary files, a readable history, everything committed, a v1.0 tag and the definitive check of cloning into a new folder and starting it up. The README is the most profitable document you will write: ten sections in order — title and one-liner, description of the problem before the solution, features, requirements, installation as copy-pastable commands, usage with a text demo of a real session, structure, tests, limitations and authorship — and it takes an hour to write.
The five-minute demo has a script and an allocation of time: thirty seconds of problem, thirty of solution, two minutes demoing the main flow with data prepared in advance and rehearsed, one minute of a technical detail you are proud of and one minute of limitations and next steps; and if something fails, you acknowledge it, note it down and carry on, without debugging live. Technical decisions are defended with the pattern context, ruled-out alternative, criterion and consequence — why a class and not a dictionary, why JSON and not CSV, why separate the interface from the logic — and the limitations with the pattern "I know, I left it out on purpose, this is how I would do it", which turns every gap into a decision and which makes the Won't box from 09-01 your script. The typical questions are prepared in writing, the AI one included, where the honest answer always wins. Criticism is heard out in full, written down verbatim, thanked for and later classified into a real bug, a sound improvement, a personal preference or out of scope; the first two become tasks. And the project gets published: a GitHub repository with a description, topics and a licence, on the understanding that two or three finished projects are worth more than twenty half-built ones, and with the habit of reading other people's code to build up reference points. The self-assessment closes the circle with the rubric from 09-01, demanding concrete evidence per criterion, and the improvement plan sorts by weight what raises the score most.
With this, the project is built, finished, defended and published — which is exactly what was set out when the module opened. One last conversation remains, and it is not a technical one: what to do from here. In Next steps as a programmer we will go back over what these nine modules have taught, what this course did not cover and is worth knowing exists, the career paths that open up with what you already know, the habits worth consolidating and a concrete plan for the next three months.
Fundamentals of Programming
Module 1: Introduction to Programming
- What is programming?
- History of programming
- Programming languages
- Development environments
- From problem to algorithm
Module 2: Core Concepts
- Variables and data types
- Operators and expressions
- Input and output
- Type conversion and data validation
Module 3: Control Structures
Module 4: Functions and Procedures
- Defining and using functions
- Parameters and return values
- Variable scope
- Breaking a program down into functions
- Functions as values: lambda and higher order
Module 5: Data Structures
- Lists and arrays
- Strings
- Dictionaries and sets
- Tuples and nested structures
- Saving data to files: text, CSV and JSON
Module 6: Basic Algorithms
Module 7: Objects and Code Organisation
- From data to objects: classes and instances
- Attributes, methods and the constructor
- Collections of objects
- Modules, packages and imports
Module 8: Good Practices and Tools
- Documentation and comments
- Debugging and error handling
- Version control
- Automated testing
- Style, readability and refactoring
