⚙️ Productivity & Craft · Chapter 2
Git and code management for one person
Git is not a backup tool. It is a record of decisions — and interviewers read it.
Study this lesson free in the app →
The basics
Git is a version-control tool: it records snapshots of your code (commits), each with a message, so you can see what changed, when, and why — and safely try changes on the side.
The problem it solves: without it you have pipeline_final_v3_REAL.py, no idea which change broke the revenue number three weeks ago, and no way to work on a new feature without risking the version that runs. Even solo, you need a record you can search.
The mental model: a scientist's lab notebook, not a backup drive. Undo is the least interesting thing it does. The valuable part is answering questions later: when did this number go wrong? (git bisect), why was this decided? (git log), what am I about to ship? (git diff).
Words you must know 5
- Commit
- a snapshot plus a message. One idea per commit; the message says why, because the diff already shows what.
- Branch
- an independent line of work (
feat/kafka-dedup) you can merge or throw away without touchingmain. - Staging
- choosing what goes into the next commit.
git add -plets you review hunk by hunk. revertvsreset- revert adds a new commit that undoes an old one (safe once pushed); reset moves history backwards (local only, destructive).
- reflog
- the log of everywhere HEAD has been; how "lost" commits come back.
And one rule outside the commands: a secret that was ever committed is compromised. Rotate it first.
Explained
$ git log --oneline
7a3f21b update
9c8e44a fix
2b1d90c update again
f40aa12 asdfversus
$ git log --oneline
7a3f21b Dedup ride events on event_id before aggregation
9c8e44a Add DLQ for unparseable payloads with reason codes
2b1d90c Partition raw landing by event date, not ingest dateSame code. One of these gets read in an interview and answers the question "how do you work?" before you say a word.
The lab notebook
A scientist doesn't keep a notebook so they can undo an experiment. They keep it so that in six months, when a result looks strange, they can find which change caused it and what they were thinking at the time.
Git is that notebook. git revert is the least interesting thing it does.
The valuable operations are the ones that answer questions: when did this number start being wrong? (git bisect), who decided this and why? (git log, git blame), what exactly am I about to ship? (git diff).
A commit message is not a formality. It is the only place the why survives — the code can only ever show the what.
The loop, for a team of one
The PR step feels absurd when you're the only developer. Do it anyway — reading your own diff as a reviewer catches things you cannot see as an author.
The commands that cover 95% of days
# --- daily ---
git status # the most-run command in the world
git switch -c feat/kafka-dedup # create and move to a branch
git add -p # stage HUNK BY HUNK — see what you're committing
git commit -m "Dedup events on event_id before aggregation"
git push -u origin feat/kafka-dedup
# --- looking around ---
git log --oneline --graph -20
git diff # unstaged changes
git diff --staged # what's about to be committed
git show 7a3f21b # one commit in full
# --- undoing, from safest to sharpest ---
git restore file.py # throw away uncommitted edits to a file
git restore --staged file.py # unstage, keep the edits
git commit --amend # fix the last message or add a file
git revert 7a3f21b # a NEW commit undoing an old one — safe on shared history
git reset --hard HEAD~1 # delete the last commit. Local only. Destructive
# --- rescue ---
git stash # park work, do something urgent, git stash pop
git reflog # every HEAD you've had — how you recover 'lost' commitsgit add -p is the highest-leverage habit here. It forces you to look at every hunk, which catches debug prints, commented-out code and stray credentials before they're history.
What makes a commit good
One commit = one idea. If the message needs an "and", it's two commits. A change you can't describe in one line is a change nobody can review — including you, later.
The message says why, not what. The diff already shows what.
✗ Update pipeline.py
✗ Fixed bug
✓ Dedup events on event_id — Kafka at-least-once was double-counting revenue
✓ Partition raw by event_date not ingest_date so backfills land correctlyA format worth adopting: imperative mood, under ~70 characters, then a blank line and a paragraph if the why needs room.
Add DLQ for unparseable payloads
A single malformed record was stalling the partition and lag climbed
until the on-call page. Records that fail parsing three times now go
to ride_events.dlq with a reason code, and the consumer commits past
them. Alert fires if the DLQ rate exceeds 0.5%.Write that and in eight months, when someone asks why the DLQ threshold is 0.5%, the answer exists.
The number went wrong three weeks ago
Daily revenue has been ~8% low since roughly the 12th. Nobody knows which change did it. There are 60 commits in that window, and the pipeline takes 20 minutes to run.
You can test any given commit by running the pipeline and checking the total.
The credential in the history
You commit a config file with a database password. You notice ten minutes later, delete the line, and commit the fix.
The password is still in the repository. Git keeps every version — that's the entire point of it. Anyone with the repo can run git log -p and read it.
If you've already pushed to GitHub, treat it as public. Bots scan public commits for credentials within minutes of a push.
What to actually do, in order:
- Rotate the credential. Immediately, before anything else. Assume it is compromised — this is not a code problem any more
- Only then clean the history (
git filter-repo, or BFG). On a shared repo this rewrites history and everyone must re-clone - Add the file to
.gitignoreand commit that - Install a pre-commit secret scanner so the next one is caught before it becomes history
And the habit that prevents it entirely: secrets live in environment variables and a secrets manager, never in a file inside the repo. A .env file is fine — as long as .env is in .gitignore from the first commit, and .env.example (with fake values) is what's tracked.
Repo layout that survives you
rideflow/
README.md what it is, how to run it, in 20 lines
.gitignore .env, __pycache__, data/, .venv
.env.example every variable, with fake values
pyproject.toml declared dependencies
uv.lock locked dependencies — commit this
src/ the code
tests/ the tests
sql/ models and migrations
docs/adr/ why decisions were madeThe two that separate a portfolio repo from a homework folder:
A README that lets a stranger run it. What problem it solves, the architecture in five lines, how to run it locally, what the trade-offs were. An interviewer opening your repo reads this and nothing else.
docs/adr/ — Architecture Decision Records. One short file per real decision: the context, the choice, the alternatives, the consequence. 0003-partition-by-event-date.md. Three paragraphs each.
ADRs are unusual in a personal project, and that's exactly why they land. They demonstrate that you make decisions deliberately rather than by default — which is the thing senior interviews are trying to establish.
Words to know
| Commit | A snapshot plus a message recording what changed and why. |
|---|---|
| Branch | An independent line of work you can develop and discard without touching main. |
| git add -p | Stage changes hunk by hunk, reviewing each one before it becomes history. |
| Pull request | A proposed change presented for review before merging — useful even solo. |
| git bisect | Binary search through history to find the commit that introduced a problem. |
| git revert | A new commit undoing an old one — safe on history others have pulled. |
| git reflog | A log of every position HEAD has held; how you recover 'lost' commits. |
| ADR | Architecture Decision Record — a short file recording a decision and its reasoning. |
| Lock file | The exact resolved dependency versions, committed so builds reproduce. |
In two minutes
- Git is a searchable record of decisions, not just a backup; the commit message is the only place the why survives.
- Daily loop even solo: pull, branch, small commits, read your own diff, PR, merge, delete the branch.
- One commit, one idea, imperative message under ~70 characters — small commits make git bisect useful.
- git add -p reviews every hunk and catches debug prints and secrets before they become history.
- Undo by risk: restore (uncommitted), amend (last local commit), revert (shared history), reset --hard (local only).
- git reflog recovers commits you reset away; bisect finds a bad commit among 60 in about 6 tests.
- A committed secret stays in history: rotate first, clean history second, then .gitignore and a pre-commit scanner.
- A portfolio repo needs a README a stranger can run, a .env.example, a lock file and docs/adr for decisions.
Test yourself
1. A bug appeared somewhere in the last 60 commits. Fastest way to find it?
- Read every diff
- git bisect — binary search, about 6 tests
- git blame on the whole file
- Revert all 60 and reapply
Show the answer
git bisect — binary search, about 6 tests — Bisect halves the search space each step: log₂(60) ≈ 6. It only works well if commits are small and individually runnable.
2. You committed a password, then deleted the line and committed again. Is it safe?
- Yes — the file no longer has it
- No — it is permanently in history; rotate the credential immediately
- Only if you never push
- Yes, after git gc
Show the answer
No — it is permanently in history; rotate the credential immediately — Git keeps every version. If pushed to a public repo, assume bots have already read it. Rotate first, clean history second.
3. What should a commit message explain?
- What lines changed
- Why the change was made
- Who made it
- When it was made
Show the answer
Why the change was made — The diff already shows what changed; Git records who and when. The why exists nowhere else — that's the message's whole job.
4. Why use git add -p instead of git add .?
- It is faster
- You review each hunk, catching debug prints, dead code and stray secrets
- It compresses better
- It is required for branches
Show the answer
You review each hunk, catching debug prints, dead code and stray secrets — It turns committing into a review step. Most accidental commits of debug code or credentials come from a reflexive git add ..
5. You need to undo a commit that others have already pulled. Use…
- git reset --hard
- git revert
- git commit --amend
- delete and re-clone
Show the answer
git revert — revert creates a new commit undoing the old one, leaving history intact. reset and amend rewrite history and break other clones.
6. You staged pipeline.py by mistake but want to keep your edits. Which command?
- git restore pipeline.py
- git reset --hard
- git restore --staged pipeline.py
- git revert pipeline.py
Show the answer
git restore --staged pipeline.py — --staged removes it from the next commit and leaves the working-copy edits alone. Plain restore throws your edits away; reset --hard throws everything away.
There are 11 questions, 3 puzzles and 7 recall cards for this lesson in the app — the recall cards come back on later days, just before you would forget.
Start this lesson →