Avoiding merge conflicts: a practical Git & GitHub guide
Merge conflicts are inevitable when multiple developers work on the same codebase, but they're far from unavoidable. The difference between a team that spends hours resolving conflicts and one that rarely encounters them isn't luck—it's discipline. In this guide, we'll walk through the habits and strategies that keep conflicts minimal and manageable, so your team stays productive and your main branch stays clean.
Understanding why conflicts happen
Before we talk prevention, let's be clear about what causes conflicts. Git is excellent at merging changes when they touch different parts of a file or different files entirely. Conflicts arise when two branches modify the same lines in the same file. The larger your changes, the longer your branch lives, and the more parallel work happening, the higher your conflict risk becomes.
The good news: this is predictable. We can engineer our workflows to reduce conflicts dramatically. Let's look at how.
Small, short-lived branches
The single most effective way to avoid merge conflicts is to keep branches small and merge them quickly. A feature branch that lives for two weeks is exponentially more likely to conflict with other work than one that lives for two days.
Here's what this looks like in practice. Instead of a branch called feature/user-dashboard that encompasses user profiles, analytics charts, and export functionality for three weeks, break it into:
feature/user-profile-display(merged after 1–2 days)feature/analytics-charts(merged after 1–2 days)feature/data-export(merged after 1–2 days)
Each branch is smaller in scope, easier to review, and less likely to overlap with parallel work from teammates. When conflicts do occur on a small branch, they're simpler to resolve because you have fewer changes to reason about.
A practical discipline: if your branch is open for code review longer than it takes to implement, that's a signal it's too large. Aim for branches that are done, tested, and ready to merge in 1–3 days of work.
In a real scenario, your backend team might be working on a new API endpoint while your frontend team needs to consume it. Instead of waiting two weeks for the complete API feature, ship a minimal version in 2–3 days. Both teams can integrate sooner, and conflicts stay isolated to specific, manageable pieces.
Pull main often
Even with small branches, your feature branch can diverge from main significantly if other work lands before you merge. The antidote is simple: pull main into your branch regularly.
A good habit is to pull main at the start of each day and before you push, so your branch stays in sync with the team's latest work. This surfaces conflicts early, when they're fresh in your mind and the code context is clear.
git fetch origin
git rebase origin/mainOr, if you prefer merging over rebasing (and your team agrees on a convention):
git fetch origin
git merge origin/mainThe key difference: rebasing rewrites your branch history and keeps it linear; merging creates a merge commit and preserves history. Either approach works; consistency across your team matters more. We tend to favor rebase workflows because they produce cleaner history and make bisecting easier later.
When you pull main into your branch and encounter a conflict, you're dealing with a handful of changes, not weeks of divergence. You understand the context, your teammate is probably still available to clarify, and the fix is quick. Compare that to discovering conflicts during code review or deployment, when context is cold and timelines are tight.
In a distributed team scenario, developers across multiple time zones are working in parallel. Pulling main first thing in the morning ensures you're not building on outdated code and catching potential conflicts before you're deep in your own feature.
Avoid mass formatting in feature branches
Formatting changes—whitespace, linting rules, import order rewrites, or dependency upgrades that touch many files—are merge conflict landmines. A single formatting PR can conflict with dozens of in-flight feature branches, even though no actual logic changed.
The rule: never mix formatting changes with feature work. If your linter configuration changes or you need to apply a mass formatting fix, do it in its own PR, merge it immediately, then have all other developers pull main. This costs you a few minutes of coordination and saves hours of conflict resolution.
A real example: your team decides to enforce stricter TypeScript rules. Instead of applying those changes in the feature branches already in flight, create a dedicated PR for the linting config and refactor. Merge it to main. Now everyone pulls main, and their branches automatically have the new rules. No conflicts.
Similarly, if you upgrade a major dependency like React or a database client, do that in a separate PR. Feature work can wait a few hours for the upgrade to land and stabilize. The alternative—merging a feature branch that includes a major dependency upgrade—creates review friction and makes it hard to isolate what broke if tests fail.
This discipline applies to automated formatting too. Tools like Prettier should run as part of your CI pipeline and be applied uniformly. Don't let developers manually reformat code on their branches. If Prettier is part of your workflow, it should run on every branch consistently, preventing the mess altogether.
Agree on file ownership for hot files
Some files are modified constantly: configuration files, shared utility modules, schema definitions, or frequently-used components. When multiple teams or developers are changing these files simultaneously, conflicts become routine.
The solution is lightweight file ownership. It doesn't mean one person controls access; it means the team agrees that certain files require coordination. Before working on a hot file, check with the owner or the team. Communicate, "I'm going to change the shared auth utility—anyone else working on it?"
Practically, this might look like:
- Authentication utilities: Auth team owns; others request changes
- Database schema: Backend lead reviews all changes
- Shared component library: Frontend tech lead approves modifications
- API contract definitions: Backend and frontend leads align before changes
In a trading platform, the order processing module is a hot file touched by risk, execution, and settlement teams. Rather than have three teams fight conflicts constantly, designate one person as the owner. Other teams make targeted changes, get approval, and minimize the risk of conflicts and logic errors.
For files that are structurally prone to conflicts (like barrel export files or deeply nested config objects), consider refactoring to reduce the surface area. Instead of a single index.ts that exports fifty modules, split it into domain-specific export files. Instead of one massive config object, break it into smaller, independently modifiable chunks.
// Before: single hot file, high conflict risk
export * from './auth'
export * from './payments'
export * from './users'
// ... 47 more lines
// After: modular exports, lower conflict risk
// auth/index.ts
export * from './tokenManager'
export * from './passwordValidator'
// payments/index.ts
export * from './stripe'
export * from './processors'Common mistakes
Leaving branches open too long while waiting for feedback. If a PR is stalled in review, don't just wait. Start the next task on a fresh branch from main. Don't let one blocked feature block your progress. Parallel work is efficient; leaving branches open indefinitely is not.
Rebasing shared branches. Never rebase a branch that's already been pushed and others are depending on. Rebasing rewrites history, and if teammates have pulled your branch, rebasing causes them serious headaches. Only rebase branches that are local or that you know no one else has cloned.
Ignoring merge conflicts during rebase. When pulling main via rebase, conflicts can happen commit-by-commit. Resolve each one carefully. Use git rebase --abort if things get messy, and switch to a merge instead.
Mass-editing files without coordination. Renaming hundreds of files, moving packages, or reorganizing folder structure should be announced to the team and ideally done when minimal other work is in flight. The disruption is worth the cleaner architecture, but timing matters.
Key takeaways
- Keep branches small and short-lived (1–3 days). Smaller scope means fewer conflicts and faster reviews.
- Pull
maindaily and before pushing. Early conflict detection is far easier than late discovery. - Separate formatting and dependency changes from feature work. Use dedicated PRs for structural changes.
- Identify hot files and establish lightweight ownership. Coordination prevents most conflicts on critical code.
- Refactor high-conflict files to reduce shared surface area. Modular structure naturally reduces conflicts.
- Communicate with your team before big changes. A quick message in Slack saves hours of conflict resolution later.






Comments
Loading comments…