September 12, 2026
BRANCHES AND WORKTREES: PARALLEL WORK WITHOUT SHARED-FOLDER COLLISIONS

Lesson 41.2 protected *time*: every change became a checkpoint you could revisit. This lesson protects the *baseline*: the accepted version of the project stays understandable while new ideas earn their way in.
Main is the baseline, not automatically production
main is the protected accepted baseline — the version the project treats as known-good and reviewable. Many teams deploy it to production. Many do not. The deployment rule is a project choice, configured in a hosting or CI setting.
So do not call main "production." Call it what it is: the line that only receives work after review and evidence. Everything experimental lives elsewhere until it passes.
A branch is an independent line from a known commit
A branch is a named independent line of work that starts from a known project version and does not affect main until accepted.
It is not a full duplicate of the project. It is not a safety shield by itself — an unreviewed branch can still be a mess. It is an isolated history and review boundary: commits made on the branch stay on the branch until the project deliberately brings them in.
Here is the model:
main: A ─────────────── D
\
feature/source-date-ui: B ── C
Read it precisely:
- A is the known baseline both lines share.
- B and C are focused commits on the branch — the date rendered beside citations, then the missing-date state handled.
maindoes not change while the experiment runs. It stays at A, understandable and demoable.- D exists only after review and merge. It is the moment the project accepts the branch's work into its history — the subject of Lesson 41.4.
While the branch lives, main remains a safe place to demo, to start a second idea, or to revert to.
Branches first, then worktrees
A branch solves the history problem: experimental commits stay out of main. But on one computer, there is still a folder problem.
Git normally gives you one working folder showing one branch at a time. Switch branches and the files change under you. Now run two AI agents in two terminal tabs against the same folder — one building the API change, one building the UI change. Each agent's uncommitted edits appear in the other's working tree. Files bleed across tasks, diffs mix, and neither handoff describes what actually happened.
A worktree fixes that. It is a separate physical folder on the same computer checked out to another branch, sharing the same .git vault. Two folders, two branches, no bleed:
research-dashboard/ → main (integration, demos)
research-dashboard-api/ → feature/source-date-api
research-dashboard-ui/ → feature/source-date-ui
Three agents can work simultaneously, each seeing only its own branch. The integration owner keeps main clean. When the work merges, the extra folders can be removed.
Current worktree behavior and commands change occasionally, so treat the GitHub worktree documentation as the command reference. The durable idea — one writer, one folder, one branch — does not change.
Separate cohesive tasks, not "frontend versus backend"
Part IX's orchestration lesson gave the preview; here is the precise rule: separate cohesive, non-overlapping tasks.
"Frontend versus backend" is an unreliable shortcut. Frontend and backend can be separate branches when they touch distinct responsibilities and share a written API contract. But if both sides change the same contract, database migration, auth rule, config, or component, splitting by layer name guarantees a collision.
Three valid separations:
- Contract split: each side owns one end of a frozen interface. API exposes
publishedAt; UI renders it. Neither renames the field. - Sequence: the API branch lands and its contract is recorded first; the UI branch builds against it second.
- One owner: anything touching auth, migrations, deployment, or shared config gets a single writer while others review read-only.
For the dashboard's source-date feature, the branch plan looks like this:
| Branch / worktree | Bounded job | May change | Must not change | Handoff |
|---|---|---|---|---|
feature/source-date-api | expose the existing date in approved research data | mapping, API test | auth, schema, deployment | sample JSON + test result |
feature/source-date-ui | show the date and missing-date state | citation component, formatter | API contract, navigation | preview screenshot + changed-file list |
review/source-date | inspect combined work | no edits | all production settings | review findings |
Every row names the job, the allowed area, the protected area, and the exact handoff. If a row cannot fill those cells, the task is not ready to parallelize.
The events finder follows the same logic: one branch adds venue display against a frozen event shape; a second branch investigates a new data source read-only. Two writers never touch the same contract at once.
Name branches like labels, not like sighs
A branch name tells the next reader what the line of work is for:
feature/source-date-uifix/export-missing-datedocs/research-context
Not test2, new, try, or final-final. Those names force every future reader to open the branch to learn what it was. The prefix (feature/, fix/, docs/, review/) states intent; the rest states scope.
Conflicts: Git refuses to guess
Sooner or later two lines edit the same lines and Git cannot combine them. That is a merge conflict. Treat it as Git refusing to guess between incompatible edits — not a disaster, and not a failure.
Resolution requires three things: a human decision about which change (or combination) is correct, a readable result, and a re-test. The affected code must be shown, read, and verified. Do not allow an AI to auto-resolve every conflict without displaying the conflicting hunks. An agent that picks "both" or "mine" to make the error go away can silently resurrect a bug or duplicate a migration.
Small, cohesive branches with frozen contracts make conflicts rare and small. Giant overlapping branches make them inevitable and terrifying. Branch discipline is conflict prevention.
Exercise: branch plus HANDOFF.md
Create a branch for one documentation change — for example, docs/research-context — and ask the AI to confirm the branch name before editing anything. Make the edit, inspect the diff, commit the focused change, and leave main untouched.
Then add a short HANDOFF.md on the branch:
# Handoff: docs/research-context
- Purpose: ...
- Branch: docs/research-context (from main at ...)
- Validation: ... (command + result)
- Next decision: merge / revise / discard, and who decides
Check your understanding
- Why does
mainstay at A while B and C are built? What creates D? - Two agents share one folder on different branches. What bleeds, and what fixes it?
- An agent offers to resolve all conflicts automatically. What do you require first?
Finish line
You are done when you have a named branch with one focused commit and a HANDOFF.md explaining its purpose, validation, and next decision — and main is untouched.
Verify: run git status and git log --oneline on both main and the branch. Confirm the branch contains exactly the intended commit, the handoff names the validation evidence, and switching to main shows none of the branch's edits.
Common failure mode: multiple AI agents editing the same folder from different terminal tabs because each "has its own branch" — but branches do not isolate uncommitted files. Recovery: stop all writers, inspect the mixed working tree, move each writer to a separate branch *and* worktree or run the overlapping work sequentially.
ARTICLE DISCUSSION
JOIN THE
CONVERSATION.
Got a question, a take, or a better way to do this? Log in and leave a comment.
