Exercises + milestone requirements owned by this stage
Evidence and gate
0 · 0/18
Artifacts · checked requirements
Why it matters
Purpose of this stage
Write programs independently
Subjects
10 subjects · 45 canonical topics
Each subject states what you should be able to do, an order to work through it in, what to watch for, and the questions a reviewer should be able to ask you at the end. None of it is tracked or scored.
How to work through this subject
An order that works, not a checklist. Nothing here is tracked — the steps sequence this subject’s own topics and add none.
Run `git init` in your learning folder and `git status` immediately, before doing anything else.
Write `.gitignore` first — virtual environment, caches, anything holding a secret — then check `git status` again and watch the noise disappear.
Make one small change, `git add` just that change, `git commit` it with a message you could read back in a month.
Run `git log` and read your own history. If a message tells you nothing, that is feedback about the message.
Add a remote with `git remote`, then `git push`, and confirm in a browser that the code is really there.
Repeat the add/commit/push cycle for every exercise this stage, until it costs you no thought at all.
What to watch for
This is deliberately the smallest useful Git. Branching, merging, conflicts and recovery are Stage 2 — reaching for them now buys confusion, not capability.
Commit when something works, not at the end of the day. A commit is a checkpoint you can describe in one sentence.
The first thing in `.gitignore` should be your virtual environment. The second should be anything holding a secret.
`git status` is the Git equivalent of `pwd`: run it when confused, before running anything else.
A commit is a decision to keep something. If you cannot say what the commit is for, it is probably two commits.
Curriculum topics
`git init`
Turns a plain folder into a repository, so Git starts tracking what changes inside it.
`git status`
Shows what you have changed, what is staged to be saved, and what Git is ignoring.
`git add`
Chooses which changes go into the next save. Nothing is recorded until you add it.
`git commit`
Records the staged changes permanently, with a message saying what you did and why.
`git log`
Lists the commits you have made, newest first — the history of your work.
`git remote`
Registers the address of a copy of your repository that lives elsewhere, usually on GitHub.
`git push`
Sends your commits to the remote copy, so the work exists somewhere other than your laptop.
`.gitignore` basics
A file listing things Git should never record — virtual environments, caches, secrets, and anything generated.
Resources for this subject
REFERENCEgittutorial git-scm.com The official introduction to the eight commands Stage 1 actually uses, and nothing beyond them. Pro Git is the Stage 2 resource; this is the smaller reference for saving and publishing work while you are still learning Python.Open
A reviewer should be able to answer these
Answer from memory, out loud, before moving on. These are not tracked and do not affect the gate — they tell you whether you actually understand this subject.
What does `git add` do that `git commit` does not?
Why does Git require you to choose what goes into a commit instead of saving everything?
What is the difference between your local repository and the remote one?
What belongs in `.gitignore`, and what goes wrong if a virtual environment is committed?
You wrote a working script but have not run any Git command yet. What is the shortest sequence that gets it onto GitHub?
What does `git log` let you reconstruct that the files on disk alone do not?
What does `git status` tell you, and when in your workflow should you run it?
Exercises
Demonstrated practice · 35% base weight
calculator
warmup ~45 min
A menu-driven calculator supporting the four basic operations, built from functions.
What to implement
One function per operation, each returning its result rather than printing.
Prompt for two numbers and an operation.
Handle division by zero without crashing.
Reject non-numeric input and re-prompt.
Loop until the user chooses to exit, rather than running once.
Done when
Every operation is a function you could import and test.
Entering `abc` or dividing by zero produces a message, not a traceback.
The program survives ten deliberately hostile inputs in a row.
Each operation function can be called from a test with no input prompt involved.
One hint, available after you have made a real attempt.
unit converter
core ~60 min
Convert between units in at least two categories, with conversion logic separated from input handling.
What to implement
Support at least two categories, e.g. temperature and length.
Each conversion is a pure function taking a value and returning a value.
The menu selects a conversion; it does not contain the maths.
Round output sensibly and say why you chose that precision.
Reject a value that is impossible for its unit, such as a length below zero.
Done when
Adding a third category requires no change to the input-handling code.
You can convert a value and convert it back to within rounding error.
Converting a value and converting it back returns the original within your stated precision.
You can state the base unit each category converts through.
One hint, available after you have made a real attempt.
word counter
warmup ~30 min
Count words in a string or file and report the total.
What to implement
Count words in a supplied string.
Decide and document what counts as a word separator.
Handle empty input and repeated whitespace.
Report both total words and total characters.
Read the text from a file as well as from a string.
Done when
Multiple consecutive spaces do not inflate the count.
You can state your definition of a word and defend it.
An empty file reports zero rather than crashing.
The counting logic is a function that never reads a file itself.
One hint, available after you have made a real attempt.
frequency counter
core ~45 min
Count how often each word appears and report the most common ones.
What to implement
Build a mapping from word to count.
Normalise case so `The` and `the` are the same word.
Strip punctuation from word edges.
Print the top N words in descending order.
Handle a tie in counts deterministically and say what rule you chose.
Done when
The counts are correct on text containing punctuation and mixed case.
You can explain why a dictionary is the right structure here.
Running it twice on the same input produces the same ordering.
The top-N logic is separate from the counting logic.
One hint, available after you have made a real attempt.
password rule checker
core ~60 min
Validate a password against a set of rules and report every failure, not just the first.
What to implement
Check minimum length, and presence of upper, lower, digit and symbol.
Return all failed rules, not the first one.
Each rule is its own function.
The checker returns data; the caller does the printing.
Report the rules that passed as well as the ones that failed.
Done when
A password failing three rules reports three failures.
Adding a new rule requires no change to existing rules.
Each rule function can be tested on its own with no other rule involved.
An empty password reports every failure rather than one.
One hint, available after you have made a real attempt.
CSV summarizer
core ~90 min
Read a CSV and report per-column summaries without using pandas.
What to implement
Read the file using the standard library only.
Report row count and column names.
Compute min, max and mean for numeric columns.
Skip or report rows that do not parse, without crashing.
Report how many rows were skipped and why, rather than dropping them silently.
Done when
A file with a malformed row produces a warning, not a traceback.
The summary is correct on a file you did not write.
A column of mixed text and numbers is reported as text, not averaged.
The summary states the row count it actually used.
One hint, available after you have made a real attempt.
JSON reader/writer
core ~60 min
Round-trip structured data to disk as JSON and back.
What to implement
Write a nested structure to a JSON file.
Read it back and confirm it is equal to the original.
Handle the file not existing on first run.
Handle the file existing but containing invalid JSON.
Write to a temporary file and replace the original only once writing succeeded.
Done when
Running the program twice in a row works, first run included.
Corrupting the file by hand produces a clear message.
An interrupted write leaves the previous file intact.
Reading a structure and writing it back produces an equal structure, not merely a similar one.
One hint, available after you have made a real attempt.
simple menu-driven CLI
core ~60 min
A looping menu that dispatches to functions and exits cleanly — the skeleton of the Expense CLI.
What to implement
Display options and read a choice in a loop.
Dispatch each choice to its own function.
Handle an invalid choice without exiting.
Provide an explicit exit that leaves the loop.
Keep every menu action in its own module-level function rather than inside the loop.
Done when
Invalid input never crashes or exits the program.
Adding a menu entry means adding a function and one mapping entry.
The loop body is short enough to read in one screen.
Every action can be called directly from a test, bypassing the menu.
One hint, available after you have made a real attempt.
basic classes
core ~60 min
Model a transaction as a class and justify why it beats a dictionary here.
What to implement
Define a class with an initialiser and at least one method.
Create several instances and use them.
Add a readable string representation.
Write down why this is a class rather than a dict.
Add one method that derives a value rather than storing it.
Done when
Printing an instance produces something useful rather than an object address.
You can defend the choice of class over dictionary in two sentences.
No caller reaches into the instance to recompute something the class should own.
You can list which fields belong to the instance and which to the class.
One hint, available after you have made a real attempt.
tests
core ~90 min
Write pytest tests that would actually fail if the behaviour regressed.
What to implement
Test at least three functions from earlier drills.
Include one test for a failure case, not only the happy path.
Break the implementation deliberately and confirm the test fails.
Fix it and confirm the test passes again.
Give each test a name that states the behaviour rather than the function.
Done when
You have watched each test fail for the right reason.
The suite runs with a single command from the project root.
A stranger could read the test names and know what the code guarantees.
No test depends on another test having run first.
One hint, available after you have made a real attempt.
Resource status is a convenience marker only. It cannot change competencies, gates, or stage completion — in any tier below.
Start here
PRIMARY
CS50's Introduction to Programming with Python
Harvard
The teaching path for this stage, assuming no prior programming. Follow it in order and do the problem sets before the drills, which are the same material at a different angle.
REFERENCEThe Python Tutorial Python Software FoundationThe official language tutorial. Use it to check exactly how a construct behaves rather than as a course — it assumes some prior programming, which is why it is a reference here and not the teaching path.The official Python Tutorial assumes some prior programming knowledge, so use it as reference while CS50P provides the beginner teaching path.Open
Need another explanation?5 supplementary resources · not part of the canonical library
Use these for a demonstrated gap, not as a parallel course. They are authored additions and can never be the primary resource for a subject.
REFERENCEgittutorial git-scm.comThe official introduction to the eight commands Stage 1 actually uses, and nothing beyond them. Pro Git is the Stage 2 resource; this is the smaller reference for saving and publishing work while you are still learning Python.Open
REFERENCEAutomate the Boring Stuff with Python Al SweigartFree full text, strongest on files, CSV and JSON — the exact material behind the Expense CLI. Use it as a second explanation when CS50P moves too fast.Open
REFERENCEpytest documentation pytestThe reference for the testing tool the stage requires. Read 'Get Started' and 'How-to guides'; skip the plugin ecosystem entirely for now.Open
REFERENCEPEP 8 — Style Guide for Python Code Python Software FoundationThe conventions every Python codebase assumes. Relevant the moment your code is read by someone else — which Stage 2 makes real.Open
REFERENCEThe Python Standard Library Python Software FoundationCheck here before installing anything. The source's framework rule is to prefer the standard library while it is educationally useful.Open
Stage gate
Readiness assessment
Not ready
Stage-specific requirements
Answer each prompt without notes. Expand a question only when you are ready to check your answer.
0/10
Implement a 150–300 line CLI application from a blank file Without asking AI to write it.
Answer
This is a demonstration, not a question. A complete one is a CLI of roughly 150–300 lines written from an empty file: it reads input, validates it, dispatches to functions in more than one module, persists data to disk, handles the missing and malformed file cases, and has tests you have watched fail. If you reached for generated code at any point, the demonstration has not happened yet.
Explain mutable vs immutable
Answer
A mutable object can be changed in place after it is created; an immutable one cannot, so any change produces a new object. Lists, dictionaries and sets are mutable; strings, integers and tuples are immutable. It matters when a value is shared: passing a mutable object into a function lets that function change what the caller still holds, while passing an immutable one cannot.
Explain list vs tuple vs set vs dict
Answer
A list is an ordered, mutable sequence that allows duplicates. A tuple is the same shape but immutable, which is why it can be used as a dictionary key. A set is unordered and holds each value once, making membership tests cheap. A dictionary maps keys to values and gives near-constant lookup by key. Choose from the question you ask most: order, uniqueness, or lookup by key.
Explain function arguments/returns
Answer
Arguments are the values a function receives; the return value is the single result it hands back. A function that prints has produced output but returned nothing, so nothing can be tested or reused. Returning a value lets the caller decide what to do with it, which is what makes the function composable and testable.
Explain exceptions
Answer
An exception is a signal that normal execution cannot continue, raised at the point the problem is detected and handled somewhere up the call stack. Catch the specific exception you anticipated and let the rest propagate, because a bare catch-all hides bugs. Validation prevents a bad state; exception handling responds to one that has already occurred.
Explain module/import
Answer
A module is a single Python file; a package is a directory of modules. Importing executes the target module once and binds its names into the importing namespace. Whether an import resolves depends on where Python believes the project root is, which is why the same code can import successfully from one directory and fail from another.
Explain class/object
Answer
A class is a template describing what data an instance holds and what it can do. An object is one instance made from that template, with its own attribute values. Reach for a class when data and behaviour travel together; a plain dictionary is still the right answer when there is no behaviour to attach.
Explain file serialization
Answer
Serialization converts in-memory objects into a form that can be written to disk, and deserialization reads it back. JSON preserves structure and basic types; CSV is text with conventions and returns every field as a string. Both need the missing-file and malformed-content cases handled, because both will occur.
Explain test vs runtime code
Answer
Runtime code is what the program does; test code is what proves it does it. Tests are not shipped as behaviour, they are run to check behaviour, and they must be able to fail. A test you have never watched fail is evidence of nothing, which is why you break the implementation deliberately before trusting a green run.
Explain what a commit records, and why `.gitignore` exists Basic Git only. Branching, merging and recovery belong to Stage 2.
Answer
A commit records a snapshot of the tracked files plus who made it, when, and why. `.gitignore` lists paths Git should not track, such as virtual environments, caches and anything holding a secret. It exists because the repository should contain the source of your project and nothing that can be regenerated or must never be shared, and because history keeps whatever you commit even after a later commit deletes it.
Universal requirements
The eight conditions 01_LEARNING_RULES.md applies to every stage. Expand one to see what it actually asks for.
0/8
Core concepts can be explained from memory.
What this asks for
Every subject in this stage has review questions. The condition is met when you can answer them out loud, without notes and without looking anything up, in your own words rather than a recited definition. An explanation you can only give with the material open is recognition, not recall.
Required exercises are complete.
What this asks for
Complete means each exercise satisfies its stated requirements and its done-when criteria, not that you attempted it. Where an exercise says you should be able to explain or defend something, that explanation is part of completing it. A stage whose source lists no exercises meets this through its owned project milestone instead.
Stage project works.
What this asks for
Works means it runs end to end from a clean checkout, on the inputs it was built for, with the failure cases it claims to handle actually handled. It is not a notebook that produced a result once. Someone else following your README should be able to run it without asking you a question.
Tests or verification exist.
What this asks for
There is something that would fail if the behaviour regressed, and you have watched it fail. That can be automated tests or a written verification procedure with recorded results, depending on what the stage produces. A test that has only ever passed is evidence of nothing.
README explains decisions.
What this asks for
The README says what the project is, how to install, run and test it, and why it is built the way it is: the choices you made, the alternatives you rejected, and what you deliberately left out. A list of commands with no reasoning does not meet this condition.
You can reproduce the core implementation without copying.
What this asks for
Delete the central piece and write it again from an empty file, without your previous version, a tutorial, or generated code in front of you. It does not have to be identical, it has to work and you have to be able to explain every line. This is the condition that separates having finished from being able.
You pass the stage self-assessment at >= 80%.
What this asks for
Record a gate exam and score yourself honestly against the dimensions the roadmap defines: recall, implementation, debugging, testing and design judgment. Elapsed time, hours watched and completed resources count for nothing here. Checking the boxes without a recorded exam does not open the gate.
Claude/ChatGPT reviewer finds no foundational blocker.
What this asks for
Ask an AI reviewer to examine the work against this stage's concepts and report foundational misunderstandings, not style. A foundational blocker is a misconception that later stages would build on, such as leakage, a broken abstraction, or an explanation that is confidently wrong. Nothing in that class may remain open when you pass.
Recorded exam
No exam recorded. Checking every requirement is not sufficient.