Lesson 3 - Working Together Without Stepping on Toes
Welcome to Working Together Without Stepping on Toes
Back in Module 3 you met the merge conflict — that moment when Git can’t decide whose change wins and hands the decision back to you. It felt dramatic the first time. But on a team, conflicts aren’t a special event; they’re a routine part of many people editing one shared codebase. The teams that suffer aren’t the ones that have conflicts — everyone has them — they’re the ones whose conflicts are huge, tangled, and rare enough to be terrifying. This lesson is about the everyday habits that keep conflicts small, frequent, and boring. None of them are new Git commands so much as new rhythms: how often you integrate, how big you let your work grow, and how clearly you coordinate with the people around you.
By the end of this lesson, you will be able to:
- Integrate early and often using
git pullandgit pull --rebaseto stay current withmain - Explain why small, short-lived branches and small pull requests reduce conflicts
- Divide work and communicate so two people don’t rewrite the same code at once
- Use shared conventions and formatters to prevent “phantom” formatting conflicts
This builds on merge conflicts (Module 3) and pull requests (Module 5). Let’s begin.
Integrate Early and Often
The single biggest cause of painful conflicts is divergence over time. While you work on your branch, your teammates keep merging their work into main. Every merge that lands on main while you’re heads-down is a difference your branch hasn’t seen yet. Wait a day and there are a few small differences. Wait three weeks and there are dozens — and now you’re untangling all of them at once, after you’ve forgotten the context of half of them.
The fix is to bring main into your branch regularly, so you reconcile small differences continuously instead of one giant painful merge at the end. A simple daily habit looks like this:
# Update your local main with the latest from the remote
git switch main
git pull
# Bring those updates into your feature branch
git switch feature/export-csv
git merge mainIf there’s a conflict, you resolve it now — while it’s small and you remember why your code looks the way it does. The alternative, git pull --rebase, does the same integration but with a cleaner history: instead of adding a merge commit, it replays your branch’s commits on top of the latest main, as if you’d started your work from there:
git pull --rebase origin mainThe result is a straight, linear history with no merge bubbles. The trade-off is that rebasing rewrites your commits, so you should only rebase a branch you haven’t shared yet (more on that in the callout). If you’d rather look before you leap, git fetch downloads the latest changes without touching your working files, so you can review what’s new before deciding to merge:
git fetch origin
git log HEAD..origin/main --oneline # see what landed on main
git merge origin/main # integrate when you're readyWhichever you choose, the principle is the same: pull before you push, and do it often.
Keep Branches and Pull Requests Small
Integrating often is much easier when there’s less to integrate — which is the second habit. The longer a branch lives and the more it changes, the further it drifts from main, and the harder the eventual reconciliation. A branch that lives one day touches a handful of files that have barely moved on main. A branch that lives three weeks touches dozens of files, many of which other people have rewritten in the meantime. The conflict risk grows with both time and size.
The same logic applies to the pull request at the end. A small PR — a few hundred lines or fewer, doing one clear thing — is quick to review, quick to merge, and unlikely to collide with anything. A giant PR sits open for days waiting for review, drifting from main the whole time, and lands as a single high-risk merge. Small, short-lived branches mean small, frequent merges, which is exactly the rhythm that keeps conflicts boring.
So scope your branches tightly: one feature, one fix, one focused change. If a task is large, split it into several branches that each merge on their own. You’ll integrate more often, your teammates will review faster, and main will never be far away.
Communicate, Divide the Work, and Use Shared Conventions
Some conflicts have nothing to do with timing — they happen because two people edited the same place at the same time. The cleanest way to avoid that is simply to not do it. Before a team starts a chunk of work, it helps to divide the territory: agree on who owns which area, so two people aren’t independently rewriting the same file or the same function. A quick message — “I’m refactoring the export module today, hold off on that file” — prevents a conflict that no tool can prevent for you. A well-structured codebase helps here too: when responsibilities are split across focused, separate files instead of crammed into one giant file, people naturally work in different places and step on each other far less.
The other quiet source of conflicts is formatting churn. Imagine you and a teammate both edit the same function, but your editor uses tabs and theirs uses spaces, or yours wraps lines at 80 characters and theirs doesn’t. Now Git sees changes on nearly every line — even lines neither of you meaningfully touched — and reports conflicts that are about whitespace, not logic. These “phantom” conflicts are pure friction. The cure is a shared formatter and linter: a tool the whole team runs (often automatically on save or before each commit) that rewrites code into one agreed style. When everyone’s code comes out formatted identically, the only differences Git sees are the real ones, and conflicts shrink to the changes that actually matter.
And when a real conflict does land, resolve it calmly with the Module 3 workflow: open the conflicted file, find the <<<<<<<, =======, and >>>>>>> markers, decide what the combined result should be, delete the markers, then stage and commit. It’s the same skill you already have — teamwork just gives you more chances to practice it.
Conflicts are normal — the goal is small and frequent, not zero
On any active team, merge conflicts are expected, not a sign that something went wrong. You can’t eliminate them and you shouldn’t try — chasing zero conflicts usually means people stop integrating, which only makes the eventual merge worse. The realistic goal is to keep conflicts small and frequent so each one is trivial to resolve. One caveat on git pull --rebase: it rewrites your commits, so only rebase a branch you haven’t pushed and shared. This is the same “don’t rewrite shared history” rule from Module 2 — rebasing a branch teammates have already pulled will leave their copies out of sync.
Practice Exercises
Exercise 1: The aging branch
Two teammates each open a feature branch off the same main. One merges their branch back the next day; the other lets theirs run for three weeks before merging. Why is the three-week-old branch far more likely to produce a painful conflict?
Hint
Conflict risk grows with both time and size. While the long branch sat open, teammates kept merging into main, so it diverged from main by dozens of changes across many files — possibly the same files the branch touched. The one-day branch only had to reconcile a handful of small, recent differences. The longer a branch lives without integrating, the more it drifts and the harder the eventual merge.
Exercise 2: Starting your day
You sit down each morning to continue work on a feature branch that’s been open for a few days. What’s a good habit to do before writing any new code, and why?
Hint
Integrate the latest main into your branch first — for example git switch main, git pull, then git switch back to your branch and git merge main (or git pull --rebase origin main). This reconciles overnight changes from teammates while they’re still small and you have fresh context, instead of letting differences pile up into one big merge later. Pull before you push, and do it often.
Exercise 3: The whitespace storm
You and a teammate edit the same function. Neither of you changed much logic, yet Git reports conflicts on almost every line. How could a shared formatter have prevented this?
Hint
The conflicts are “phantom” formatting conflicts — different indentation, line wrapping, or tabs-vs-spaces settings made Git see changes on lines neither of you meaningfully altered. A shared formatter/linter run by the whole team rewrites everyone’s code into one agreed style, so the only differences Git sees are real logic changes. That removes formatting churn entirely and keeps conflicts limited to the changes that actually matter.
Summary
Most merge conflicts are preventable with a few team habits. Integrate early and often: pull main into your branch regularly with git pull (or git pull --rebase for a linear history, or git fetch to review first) so you reconcile small differences continuously instead of one giant merge. Keep branches and PRs small and short-lived: the longer and bigger a branch grows, the further it drifts from main and the harder the conflict. Communicate and divide the work so two people aren’t rewriting the same file or function at once, and structure the codebase into focused files. Lean on shared conventions — a common formatter and linter eliminate phantom formatting conflicts so Git only flags real changes. When a conflict does happen, it’s normal; resolve it calmly with the Module 3 workflow.
Key Concepts
- Integrate early and often — pull
maininto your branch frequently to keep differences small. git pull --rebase— replays your un-pushed commits on top ofmainfor a cleaner, linear history.- Small, short-lived branches/PRs — less time and less size means less divergence and easier merges.
- Shared conventions — a common formatter/linter prevents phantom conflicts from formatting churn.
Why This Matters
Conflicts are the part of teamwork that scares newcomers most, yet they’re entirely manageable once you treat them as routine rather than as failures. The habits in this lesson — integrate often, stay small, coordinate, standardize — are what separate a smooth shared repository from a stressful one, and they cost almost nothing once they become reflex. Adopt them and you’ll spend your energy on the work itself instead of untangling avoidable merges. With collaboration habits in place, the next lesson turns to marking the milestones that matter: tagging versions and cutting releases.
Next Steps
Continue to Lesson 4 - Tags and Releases
Mark meaningful points in history with tags and turn them into published releases.
Back to Module Overview
Return to the Team Workflows module overview
Continue Building Your Skills
You can now keep a multi-person repository running smoothly: integrating early and often, scoping branches and pull requests small, dividing the work, and leaning on shared conventions to keep conflicts tiny. Next you’ll learn to mark the moments that matter in a project’s history — tagging versions and publishing releases the whole team can point to.