Beyond Vibe Coding Patterns for Building Real Software with Claude Code

Version Control

On this page

Part I chapter draft. The one big landmark the directory chapter flags but doesn’t cover. This is a narrative chapter, not a Briefing entry, because version control is load-bearing for half the book that follows — and because the book-specific argument (Git is the instrument panel for supervising Claude, not developer bureaucracy) needs room to land. Non-devs read; devs skip. Working title.


Why this chapter exists

Most explanations of Git start by telling you it’s how programmers track changes to their code, and then drown you in commands. That framing is true and useless. It treats version control as hygiene — something disciplined developers do, like flossing.

Here is the framing that actually matters for you: version control is the cockpit from which you supervise an AI that writes code faster than you can read it.

Think about what you’re actually doing when you work with Claude. It edits files. It creates files. It deletes files. It does this quickly, confidently, and sometimes wrongly — and the whole thesis of this book is that catching the wrongly is the job. But you cannot catch what you cannot see, you cannot undo what you didn’t checkpoint, and you cannot review a change you have no record of. Version control is what turns “Claude edited some stuff” into a reviewable, reversible, accountable act.

Without it, letting Claude write code is like letting someone rearrange your house while you’re out, with no before-photos and no way to put anything back. With it, every session becomes a set of labeled, inspectable, undoable changes. That is not bureaucracy. That is the difference between supervising an AI programmer and merely hoping.

So this chapter teaches version control as the supervision layer — but not the way newcomers expect. It gives you three real superpowers for supervising an AI: undo it when it’s wrong, isolate it so it can’t hurt what already works, and keep a record of what changed and why. The fourth thing you’d assume you need — review every change by reading it — is the one you mostly hand off to machines, for reasons we’ll get to. Supervising Claude turns out to be far less about reading its code than the newcomer fears.


What version control actually is

Strip away the jargon and version control is a time machine for a folder full of files.

You’ve met weaker versions of the idea. “Track Changes” in Word shows you what was edited. Dropbox and Google Docs quietly keep old versions you can roll back to. Video games have save points you can return to when a boss fight goes badly. Version control is all of those, made deliberate and precise: instead of the system autosaving whenever it feels like it, you decide the save points, you label each one, and you can jump between any two of them and see exactly what differs.

The specific system nearly everyone uses is called Git. It was built in 2005 by Linus Torvalds (the same person who started Linux) to manage the Linux source code, and it won so completely that “version control” and “Git” are now almost synonyms. There are others — Mercurial, Subversion — but if this book says “version control,” it means Git, because Claude does too.

A folder that Git is tracking is called a repository, or repo for short. That’s the single most common piece of vocabulary you’ll hear: “initialize a repo,” “commit to the repo,” “clone the repo.” A repo is just your project folder plus a hidden .git subfolder (a dotfile folder — see the Dotfiles Briefing) where Git keeps the entire history. Delete that hidden folder and you have an ordinary folder again with no memory; the history lives entirely inside it.


The one confusion to clear up first: Git is not GitHub

This trips up almost every newcomer, so let’s kill it now.

  • Git is the tool that runs on your computer and records the history of your files. It needs no internet and no account. It is free software you install.
  • GitHub is a website — a company (owned by Microsoft) that hosts copies of Git repositories online, so you can back them up, share them, and collaborate. It is one of several such hosts; GitLab and Bitbucket are competitors that do the same job.

The relationship is like the difference between writing (something you can do alone, on paper, with no one watching) and publishing to a particular website (a service you sign up for). You can use Git for years and never touch GitHub. But most projects do push their repo up to GitHub so there’s an off-machine backup and a place for others to see it — and connecting your machine to GitHub is exactly where SSH keys come in (see the SSH keys Briefing).

When Claude says “commit this,” that’s pure Git, happening locally. When it says “push to GitHub” or “open a pull request,” that’s the hosted service. Keeping the two straight will save you an enormous amount of confusion, because their vocabularies overlap but their jobs don’t.


The loop you’ll actually run

Day to day, working in a repo is a short cycle repeated endlessly. Here it is, in the order it happens, with the command Claude will use and what it’s really doing.

1. See what changed — git status and git diff. git status answers “what state am I in?” — which files were modified, created, or deleted since the last save point. git diff goes deeper and shows the actual line-by-line changes. You’ll lean on status mainly as an orientation tool — when a session has gone sideways and you need to get your bearings — not as a routine check. And diff, despite being the command that shows you everything, is one you’ll read far less than you’d guess; we’ll come back to why.

2. Choose what goes in — git add (staging). Git makes you select which changes belong in the next checkpoint, rather than blindly saving everything. This selection area is called the staging area (or the “index”), and git add file.js puts a file’s changes into it. The point is control: you might want this checkpoint to be “fixed the login bug” without also including some half-finished experiment sitting in another file. Staging is how you keep each save point clean and meaningful.

3. Make the checkpoint — git commit. A commit is one labeled save point: a snapshot of everything staged, stamped with your name, the time, and a commit message you write describing the change (git commit -m "Fix login redirect"). This is the atomic unit of everything in Git. When people say “revert that commit” or “which commit broke it?”, the commit is the thing they’re pointing at. Each one has a unique ID (a long string of letters and numbers) so you can always name a specific point in history.

4. Look back — git log. git log is the list of every commit, newest first — the project’s diary. The form you’ll actually live in is the compact one, git log --oneline -20 (twenty commits, one line each); people run it so constantly they alias it — a common one is glo — because it’s a first-reach orientation tool: it tells you at a glance what’s committed in the folder you’re standing in, which is how you see what a branch or worktree contains that main doesn’t, or confirm a change landed where you meant it. A repo with good commit messages reads as a story of how the code got here; one with commits all named “stuff” and “fix” is a diary written by someone who didn’t want to be understood. Alongside git status, git log --oneline is the other command you reach for when you’ve lost the thread of where things stand.

5. Sync with the world — git push and git pull. If the repo is connected to GitHub, git push uploads your new commits to the hosted copy (the remote, usually nicknamed origin), and git pull downloads commits other people (or other machines) have added. The habit that avoids grief: pull before you push, so you’re building on the latest version rather than colliding with it.

That’s the loop: status → add → commit → (log) → push. Everything else in Git is a variation on, or a recovery from, this cycle.


Reviewing: the job you hand to machines

Here’s the counterintuitive part, and it’s worth stating plainly because every instinct says otherwise: experienced people who work with AI all day barely read diffs at all. Not out of laziness — out of a better strategy. If you had asked me to guess before I understood the practice, I’d have told you the diff was the review surface and reading it was the whole game. It isn’t, and pretending otherwise would be exactly the kind of plausible-but-wrong advice this book exists to catch.

Two things explain why the diff recedes.

First, reading every change doesn’t scale and isn’t where your judgment lives. When Claude writes most of the code, proofreading its output line by line is both impossible at speed and beside the point. Your value is deciding what to build and judging whether the result behaves correctly — the product-and-project-manager altitude — not inspecting the implementation. You supervise by specification and outcome: does it do what I asked, and did it touch only what it should? That’s a different question than “is line 47 correct,” and a better one.

The distinction, named: supervise, don’t review. Two different jobs hide under the phrase “checking Claude’s work.” Reviewing is reading the implementation — line by line, is this code right. Supervising is judging by specification and outcome — did it do what I asked, and did it touch only what it should. Your job is supervision; review you delegate to gates. The classical anchor is black-box vs. white-box: supervision judges the thing by its behavior at the interface (black-box); review inspects the internals (white-box) — and the gates do that white-box work for you. This distinction runs through the whole book; it’s the operational form of “Claude is the programmer, you are the engineer” — the principle Chapter 4, The Engineer, Not the Programmer is built around.

Second — and this is the book’s own spine (Chapter 6, Correct by Design) — the reviewing that does matter gets converted into machines that do it for you. A test that must fail, a linter, a type checker, a CI pipeline, an automated code-review pass — these read the diff so you don’t. That is the mature move: wherever “check the change” can be expressed as deterministic code, convert it, and there’s nothing left for your eyes to validate. Hand-reading diffs is precisely the manual vigilance the determinism chapter tells you to engineer away. When an experienced developer sets up an automated review pass, understand what happened: they didn’t stop reviewing — they promoted review from a human chore to a gate.

So where does that leave the diff? Three honest, narrow roles:

  • Machine-read, as a gate. The diff is the input to your automated review — tests, auditors, CI, an AI review pass. This is its primary role, and it happens without you reading a line.
  • A ladder, if you want to build the eye. For a reader growing toward engineering instinct, deliberately reading diffs is how you develop it — the way an apprentice learns by watching over a craftsperson’s shoulder. Valuable as practice, not as a per-commit obligation.
  • A last-resort spot-check. When something has genuinely gone sideways — the gates disagree, the behavior is wrong and you can’t tell why — you open git status to orient and git diff to look. Rare, and a diagnostic, not a routine.

What about the pathologies that never break a test — Speculative Complexity, Convention Amnesia, the quietly-swallowed error that won’t bite until later? These are real, and behavior alone won’t surface them. But the answer still isn’t your eyeballs on every diff; it’s a gate tuned to catch them — a style auditor, a structural linter, an automated review prompt that knows your conventions. The latent-pathology catch is also a machine, just a smarter one. (The pathology catalog — Chapter 9, Additive Bias and Calling the Question — pairs each pattern with the kind of gate that catches it.)

The reflex that follows. Once you’ve named it — supervise, don’t review — the move becomes automatic: build the gate that reads the diff for you, then judge the behavior. Reading diffs by hand is where you start, on the way to not needing to.


Branches: a sandbox for Claude’s riskier ideas

A branch is a parallel line of work — a copy of the project where you can make changes without disturbing the version that currently works. You do your experiment on the branch; if it pans out, you merge it back into the main line; if it doesn’t, you throw the branch away and the main line was never touched.

The default main line is conventionally named main (older repos call it master). When Claude “creates a branch,” it’s opening a sandbox.

This is tailor-made for supervising an AI. When you’re about to ask Claude for something speculative — a big refactor, a risky new feature, a “try a completely different approach” — putting it on a branch means the working version stays safe no matter how the experiment goes. If Claude produces something good, you merge. If it produces a mess, you abandon the branch and you’ve lost nothing. It’s the difference between trying a renovation on a spare room versus on the room you’re currently living in.

On GitHub, branches connect to one more concept you’ll hear constantly: the pull request (or PR). A pull request is GitHub’s formal way of proposing “merge this branch into main” — with a page where the change can be reviewed, discussed, and approved before it lands. Note the pattern again: branches and merging are Git; pull requests are GitHub’s layer on top. Pull requests become central in the team chapters (Chapter 13, Team Adoption & Code-Review Culture and Chapter 11, Testing When Claude Writes the Tests Too), where “review before merge” is how a team keeps AI-written code honest.


Worktrees: giving each Claude its own room

Branches let you keep experiments off the main line — but by default you can only occupy one branch at a time, because your project folder holds a single checked-out version. The moment you want two Claude sessions working at once, that one folder becomes a bottleneck: they’d trip over each other’s files.

A worktree removes that limit. It lets one repository have several working folders at the same time, each checked out to its own branch — parallel rooms in the same house, all sharing the same history. Claude can refactor in one worktree while a second session builds a feature in another, while your main folder sits untouched, and none of them collide.

This turns out to be one of the most important techniques for using AI effectively, because it’s what lets you run Claude as a fleet rather than one-task-at-a-time: parallel, isolated, and safe to abandon (delete a worktree and its experiment is simply gone). You won’t set these up by hand — you either direct Claude to create them (the everyday case; branch-wrangling is programmer’s work you delegate) or let a tool automate the whole lifecycle. What you own is understanding them well enough to give that direction and to find your files across the several folders they create. It gets its own full treatment in Chapter TK, Running Claude in Parallel.


Undo: the recovery moves, from gentle to dangerous

The reason version control lets you work fearlessly with Claude is that almost anything can be undone — if you checkpointed. Here are the ways back, arranged from safest to most powerful, because they are emphatically not equal.

Discard changes you haven’t committed yet — git restore (safe, but final for that work). If Claude made edits you don’t want and you haven’t committed them, git restore file.js throws those specific edits away and returns the file to its last committed state. Safe for the repo; just remember the discarded work is gone.

Shelve changes to deal with later — git stash (safe). git stash tucks your uncommitted changes aside so your working folder is clean, then git stash pop brings them back. Useful when you need to jump to something else without committing a half-done change.

Undo a commit that’s already been made — git revert (safe, the right tool for shared work). git revert creates a new commit that cancels out an earlier one. Crucially, it doesn’t erase history — it adds a “this undoes that” entry — which makes it safe even for commits you’ve already pushed to GitHub and others may have. This is usually the undo you want.

Rewrite history — git reset (powerful, and where you slow down). git reset moves your branch backward to an earlier commit, and in its most aggressive form, git reset --hard, it discards everything after that point with no confirmation and no easy path back. It’s the right tool sometimes, but it’s also the command most likely to destroy work if misused, especially on commits you’ve already shared.

Honesty tag. status, diff, log, commit, restore, stash, and revert are safe for anyone to run and reason about — they either show you things or undo things without burning the record. git reset --hard, force-pushing, and resolving merge conflicts (next) are the moments to slow down, and reasonable places to have a developer nearby the first few times. The general rule: commands that add to history are forgiving; commands that rewrite history are not.


Merge conflicts: normal, not a crisis

At some point Git will refuse to merge two branches automatically and announce a merge conflict. This alarms people far more than it should.

A conflict simply means two changes touched the same lines of the same file, and Git — correctly — won’t guess which one you meant. It marks the disputed spot in the file with <<<<<<<, =======, and >>>>>>> dividers showing both versions, and waits for a human to choose. You resolve it by editing the file so it reads the way you want, deleting the markers, and committing.

That’s the whole thing. It isn’t an error you caused or a sign something is broken; it’s Git deferring a judgment call to you because only you know the intent. It’s also, notably, a place where Claude can genuinely help — it’s good at proposing a sensible resolution — but the choice of which change wins is a judgment call, so read the result rather than accepting it blind. (First few times, this is a fine moment for a second pair of eyes.)


What not to track: .gitignore and the secrets trap

Not everything in a project folder belongs in the repo. Some files are generated and would just be noise (build output, the huge node_modules folder of downloaded libraries). And some files must never go in, because a repo — especially one pushed to GitHub — is a published record.

The mechanism is a file named .gitignore: a plain list of paths Git should pretend it doesn’t see. Claude sets these up routinely.

The part that matters for your safety: never commit secrets. API keys, passwords, tokens, and the private half of your SSH keys (see the SSH keys and Environment variables Briefings) must stay out of the repo. This is not a nagging best practice — it’s a real and common way people get compromised, because once a secret is committed and pushed, it’s effectively public and stays in the history even if you delete it later. When Claude suggests putting a key in an environment variable rather than in a file, or adding something to .gitignore, this is why. The security chapter (Chapter 12, Security & Cost at SME Scale) treats this in depth; for now, the reflex to build is: a secret never becomes a commit.


The vocabulary, in one place

The terms Claude will say, with the one-line version so they stop being noise:

TermWhat it means
repository / repoA folder Git is tracking, history and all.
commitOne labeled save point; the atomic unit of change.
staging areaThe waiting room where you pick what goes in the next commit.
diffThe line-by-line view of what changed — mostly read by machines (gates), not by you.
branchA parallel line of work; a sandbox off the main line.
worktreeSeveral of the repo’s branches checked out at once, in separate folders — the key to running Claude in parallel.
mergeCombining a branch back into another.
merge conflictTwo changes touched the same lines; Git asks you to choose.
remote / originThe hosted copy of the repo (e.g., on GitHub).
push / pullUpload your commits / download others'.
cloneMake a local copy of a remote repo.
main / masterThe default primary branch.
HEADGit’s pointer to where you currently are in history.
pull request (PR)GitHub’s proposal-and-review wrapper around a merge.
.gitignoreThe list of files Git should not track.
revert / resetUndo by adding a canceling commit / by rewriting history.

Where this hooks into the rest of the book

Version control isn’t a one-time setup you can forget. It’s the substrate the later chapters stand on:

  • Catching pathologies (Chapter 9, Additive Bias and Calling the Question) happens mostly through gates — auditors and automated review that read the diff for you — not by hand. That chapter pairs each pattern with the kind of check that catches it, and its recovery moves assume you can revert what Claude did.
  • Running Claude in parallel (Chapter TK, Running Claude in Parallel) builds directly on branches and worktrees — the isolation that lets a fleet of sessions work without colliding.
  • Tech debt in an AI-authored codebase (Chapter 10, Tech Debt in an AI-Authored Codebase) is legible only through history — the commit log is the record of how the debt accumulated.
  • Testing (Chapter 11, Testing When Claude Writes the Tests Too) and team adoption (Chapter 13, Team Adoption & Code-Review Culture) both run through pull requests and review-before-merge.
  • The control stack (Chapter 7, The Control Stack) — CLAUDE.md, hooks, and the rest — lives inside the repo and is itself version-controlled, so your steering of Claude has a history too.

If Part I gives you the vocabulary to follow the book, this chapter gives you the instruments you’ll use in every chapter after it: the undo that lets you work fearlessly, the isolation that lets you run Claude in parallel, and the record every later chapter reads from. The reviewing you’d expect to do by hand, you’ll teach machines to do instead — and that, too, is the job the whole book is about.


Draft notes (not for the reader)

  • The distinctive frame is “cockpit / supervision layer,” not “developer hygiene” — and the three superpowers are undo, isolation, and record, NOT review. Reviewing is delegated to gates (the book’s determinism spine). This was corrected in draft after the author noted that experienced practitioners essentially never hand-read diffs when working with AI. Keep the reframe load-bearing; if a section drifts back toward “read the diff,” it’s regressing.
  • git status is an orientation / recovery tool, not a habit — reached for when a session has gone sideways, per the author’s own practice. Don’t re-inflate it into a routine per-commit check. git log --oneline -20 (aliased glo) is the second orientation command the author flagged — it’s how you read what’s in main vs. a branch/worktree; keep the two paired.
  • Named concept supervise, don’t review — this is the Part I preview. The blockquote here names and defines it (anchored to black-box/white-box) and forward-refs its canonical home, Chapter 4, The Engineer, Not the Programmer (now written), where it gets the full treatment as the operational form of “Claude programs, you engineer.” Also catalogued for lookup as principle #5 in engineering-principles-catalog.md. Keep the three in sync (preview / narrative / catalog) if the framing shifts. Reading order note: this Part I chapter is read before Ch 4, so preview-here / canonical-there is deliberate, not accidental.
  • Worktree execution corrected: the reader directs Claude (or a tool) to manage worktrees; they don’t run git worktree by hand. Their job is understanding the concept + directory layout, to guide Claude and to find files. Kept consistent with Chapter TK, Running Claude in Parallel.
  • The diff keeps three narrow roles only: machine-read gate input, an optional ladder for building instinct, and a rare last-resort spot-check. If the case studies (Appendix C, Annotated Case Studies) yield a good annotated diff, it belongs in the ladder framing (learning to see), never as a per-commit mandate.
  • Placement: this is a Part I chapter, almost certainly early — it pairs with Building Blocks and the SSH keys Briefing. Number left at TK per the deferral method; set it in the final renumber pass.
  • Audience tags: kept the book’s honesty convention (which commands are safe for anyone vs. call-a-developer). The reset/force-push/merge-conflict line is the main “recognition stops here” boundary.
  • Cross-refs use xref tokens (Chapter 9, etc.) so the renumber pass resolves them. Briefings are referenced by name/file link, not number.
  • Worktrees extracted: the parallel-AI material lives in its own chapter (Chapter TK, Running Claude in Parallel); this chapter keeps only the short bridge. If it still runs long, branches/undo/conflicts could move to a “Git recovery moves” Briefing — decide after a full read.

Found something wrong, unclear, or plainly disagreeable? Open an issue