58

I am still a software engineer in training, but I have heard the following adage multiple times:

Good coders don't have to deal with merge conflicts.

Whenever I work on a project with others, I'm losing an annoying amount of time and effort on resolving merge conflicts. So, I began wondering how true that adage is. I am aware of techniques like GitFlow and agile which are able to make developers efficiently split up their work, commit more often, and maintain multiple branches of code, so merge conflicts are a lot less likely. But surely they still happen and can potentially wreak havoc?

In other words, how much time and effort do seasoned developers lose to resolving merge conflicts (when they could be working on their projects)? Are available techniques able to really negate any effects of merge conflicts?

  • 183
    Good coders don't have to deal with merge conflicts. -- That statement is ridiculous. There are all sorts of factors that conspire against having freedom from merge conflicts. – Robert Harvey Dec 27 '19 at 21:16
  • 16
    That said, if it happens a lot, consider the possibility that your code is too tightly coupled, your classes are too large, and your lines of code are too long. All of those things will aggravate the problem. If only one person works on a given class at one time, merge conflicts become non-existent. – Robert Harvey Dec 27 '19 at 21:17
  • 63
    I think this fixes it: *Alone coders don't have to deal with merge conflicts. – Theraot Dec 27 '19 at 21:58
  • 56
    @Theraot Hah! I wish! I've given myself plenty of merge conflicts when merging branches. – Charles E. Grant Dec 27 '19 at 22:27
  • 24
    @Theraot nah, I had my share of conflicts even on 1-person projects. A feature is started, put on hold, dev is refactored, the feature is restarted and here we go again. – Agent_L Dec 28 '19 at 15:54
97

I think it's a little disingenuous to say that good developers never have merge conflicts, but they can surely reduce the number of times it happens. It's also very important to remember that software development is a team activity. Actions of other members of the teams can also increase or decrease the likelihood of merge conflicts.

First, it's important to understand the common ways that merge conflicts happen:

  • Two people make different changes to the same line of code
  • Someone decides to reformat the entire file so that every line is changed
  • Deleting a file and then replacing it (that's a tree conflict)
  • Code is deleted and added at the same time by two different people (not uncommon with Visual Studio .vsproj files)

Development teams have come up with ways to avoid these situations:

  • Ensure that each team member is working in different parts of the code (i.e. handled in task assignment)
  • Code standards that dictate whether you use spaces or tabs, naming standards etc. so that whole-sale code reformatting is not necessary
  • GitFlow so that everyone is working on their own branch, and can resolve conflicts at the Pull Request stage (all downstream merges work wonderfully)
  • Committing often and routinely merging from develop to ensure your feature branch is never too far out of date
  • Making sure your features are small enough they can be done in 1-3 days.

With changes like that, you can greatly minimize the likelihood of conflicts. You'll never be able to completely eliminate them.

| improve this answer | |
  • Especially keeping features small and keeping people on different parts of the code are things that are relatively easy to implement and manage. Using good commit messages helps a lot with clean-up should a conflict arise anyway. Not using a code standard will increase the amount of merge conflicts big-time, so having one of those is somewhat essential when using version control. – Mast Dec 28 '19 at 17:50
  • Good point that tools can introduce conflicting changes to project files; such changes can be impossible or at least very problematic to avoid. – UuDdLrLrSs Dec 29 '19 at 2:51
  • 10
    For the record, GitFlow isn't the only way to have everyone working on their own branch. Search for "GitFlow considered harmful" to read some alternatives, and to see several other opinions on the subject. – TheRubberDuck Dec 29 '19 at 14:51
  • 1
    In my professional experience this is spot on. Sequencing is important when working with teams. As a dev lead for most of my career, I'm a fan of small, easily code reviewable, easy testable change sets. Make tickets for renaming variables, or mini refactors to prevent bloated change sets. I'm also a fan of everyone using the same tools and enforcing styles with eslint, etc to minimize some of the mentioned bullet points. In any case, conflicts happen and "good coders" job is to mitigate the damage when it occurs, not avoid it all together. – Blaine Garrett Dec 29 '19 at 14:55
  • 1
    @DavidHammen, I think that is a false conclusion. Even when working on research problems, I commit routinely even if the work is not complete. I can do that with confidence because that work is on it's own branch. However, if your research is taking weeks or months, then perhaps the focus of your research is too broad. The idea is to narrow the scope as much as is reasonable. You may even need multiple branches for the same research problem if the approaches you are taking cannot be shared. You can easily abandon branches of research that are not working out. – Berin Loritsch Dec 29 '19 at 23:37
27

Probably the quote is incomplete:

Good coders don't have to deal with merge conflicts...
... because good coders push force.

Seriously, though, the only way to avoid merge conflicts is to work alone (and even that doesn't help; happened to me to have merge conflicts with myself on personal projects). In a company hiring experienced developers, there are however three factors which reduce the pain of merge conflicts:

  1. Excellent communication within the team.

    That is, you know what your coworkers are working on, and they know what you do. Therefore, when you're possibly modifying an area that somebody from your team is working on, you ask (or you tell). This way, you reduce a lot the risk of a painful commit.

    This applies especially to those of the refactoring tasks which could substantially affect a large part of the code base, or do changes which are painful to merge. Say you're changing the type of a given variable everywhere, while your colleague is implementing a feature where this variable is used. Not only you risk to have a painful merge conflict, but also to break the build during the merge.

  2. Great architecture.

    If you're working on a legacy code base with 4K-LOC files and need for any basic change to go modify dozens of files, there are chances that at every change, you'll get a merge conflict.

    On the other hand, when the architecture is good enough, you reduce the risk of having a merge conflict to two situations: (1) where you modify an interface, or (2) where two coworkers are modifying the exact same part of the application. In the first case, you communicate, reducing even more the risk of a painful merge. In the second case, you do pair programming, removing the merge completely.

  3. Proper interfacing with other teams.

    Sometimes, it happens to have merge conflicts between different teams. Say one team is modifying a module, while another one is modifying the code which uses this module.

    Experienced developers would tend to have adopt proper interfaces, such as SOA, which would make separate parts of the system more technically independent. Moreover, they will rely much more on specific practices such as push requests to avoid modifying someone else's code directly.

    This is essentially the first two points, but cross-teams: you communicate better, and you ensure the parts are more intermingled.

| improve this answer | |
  • 2
    Yes, but, in more cases than we are willing to admit, a revision control system is (mis)used as a way to avoid communication... Pull request comment cycle times makes for a really effective way to slow everything down. – Peter Mortensen Dec 29 '19 at 17:10
22

One colleague of mine ran into a developer who didn't have to deal with merge conflicts.

He made some changes, merged them, and a few days later the changes were gone. He found out that someone else (on a different continent as well) had replaced files instead of merging.

He put the changes back in, slightly miffed - and a few days later the changes were gone again.

That's when he had a long talk with his manager, who in turn had a long talk with the other team's manager, which may or may not have included threats of physical violence, and the other team's manager apparently had a long talk with the developer in question, and the behaviour stopped.

That said, experienced developers can avoid some merge conflicts.

  1. When you rename a file, do one merge where renaming the file (and obviously changes caused by this, like changing include statements or project files) is absolutely the only change.

  2. Make sure that everyone's editor settings are the same, so that you don't create changes just by looking at code.

  3. You get merge conflicts if two people change code in the same place. So if you add to a file, don't add at the end, but add at the logically correct place. That way if another developer does the same, merge conflicts are much less likely.

  4. Merge the common code base into your branch from time to time, so that you never have many merge conflicts. One conflict is easy, 10 in one merge are a problem.

  5. Merging from your branch to the common code base must never cause a conflict. That's because you did (4) just before you tried to do the merge, so the merge will end up taking your code without any change.

Just noticed: Yes, it is true, good developers never have merge conflicts merging from their branch into the common code base. Because any merge conflicts have been handled earlier.

| improve this answer | |
  • 2
    #3 is also important for minimising the accumulation of technical debt. A code file need not be a geological record of what happened over time. You don't have to put everything new at the end. Put it where it belongs! Tidy as you go, folks! – Lightness Races in Orbit Dec 28 '19 at 23:34
  • 2
    #5 while true is perhaps a little oversimplified. It's easy to do #4 then accrue conflicts when someone starts merging backlogged PRs (including other people's). An extra invocation of #4 after you've nominally already done it is not too uncommon. – Lightness Races in Orbit Dec 28 '19 at 23:35
7

As other answers have described, merge conflicts happen regardless of experience or skill, and an adage that claims that they're some kind of weakness that comes from lack of skill is ridiculous.

Other answers have noted ways that skilled developers can learn to help make merge conflicts less likely, from preventing formatting chaos to frequent merges to better team coordination, but I do think there's some truth to the broader concept you raise: skilled developers can become better at dealing with merge conflicts when they do occur.

Resolving merge conflicts is tricky. You have to find the conflicts, understand the conflict markers, figure out what changed on each side considering the context of the code (possibly involving hard-to-spot issues like formatting), perhaps look up individual commits from both sides of the merge to understand the original intent, actually resolve the conflict, address possible implications of the change elsewhere in the codebase (or even in other repositories if APIs or interfaces are involved), compile, test, commit, etc... You have to remember how to get the version control system to do what you want during this process. And then there are more complicated cases: conflicts involving moved/deleted files and directories; conflicts involving binary files; conflicts involving generated files (including things like Visual Studio and Xcode config files; even if the conflict is simple, the file itself will be unfamiliar until you have some experience with it); conflicts nasty enough where you go find the person on the other side of the merge and figure it out together; all sorts of fun.

That's all a lot of stuff to deal with. It can be a bit panic-inducing: you're cruising along excited to wrap something up, and suddenly you're faced with error messages, weird ">>>>>>>" stuff in the middle of your files, and a bunch of unfamiliar code that suddenly appeared from somewhere else. Like any other programming task, experience can help build the skills to deal with it more efficiently and less painfully. The first time you encounter a merge conflict, it's a confusing ordeal, while the 500th time is more routine. By this point:

  • You're skilled at using the version control system to get the information you need and to make the changes you want.
  • You're familiar with the conflict format, reading diffs, and visualizing the merge's changes.
  • You may have picked up tools, open source or commercial, to help with merges and learned how to use them effectively. These can help immensely, so much so that I'll put it in bold: good tools can help make merge conflicts much less painful.
  • You know what kinds of conflicts are typical in your projects (IDE configuration files, the bottom of string files, other hotspots that change frequently) and either adjust processes to prevent them or become adept at resolving them really quickly. Common conflicts like "oh, we both added new strings to the bottom of the file" become easy to just fix.
  • You can anticipate what types of refactoring tasks are likely to cause conflicts with the rest of your team and coordinate to reduce pain.
  • You know when not to merge. If you attempt a merge and discover that you've added a parameter to a function someone else has deleted, it might be better to stop and rethink your strategy outside the scope of the conflict resolution process.
  • You know when you've made a real mess of things and it will be easier to start the merge over again.
  • You know when the underlying codebase has changed too much to make "merging" a fair description for what you're doing, so you note the changes you actually want to make, throw your changes away, and start again with a new clean HEAD. For example, if your change is just to add a new image file, and in the meantime someone has come along and turned all the project's images into sprites, you're not really merging anymore so much as doing the task over a different way, so blow away your previous effort and do it the new way.

With all this, most merge conflicts become routine, so less time and effort is spent dealing with them. They still happen, and occasionally cause significant frustration, but usually wreak far less havoc.

| improve this answer | |
5

It's not so much the existence of merge conflicts, but how much of a problem they cause.

The 'damage' from merge conflicts with inexperienced developers is seen as being a huge issue. People invent crazy source control schemes or even stop using source control all together to avoid these 'massive' issues.

But with an experienced team, merge conflicts cause hardly any problems and are resolved quickly.

I think there are two main differences of approach which contribute to this:

  1. Using source control properly. Feature branches, lots of small commits, pulling before pushing, testing before pushing, etc.

  2. Understanding other peoples' changes. If you understand the feature and code changes that have conflicted with your change then merging the two is simple.

Pay attention to other tasks at stand-ups, talk with other people about how they are implementing the change and where you may conflict. Agree on how things should work in advance.

| improve this answer | |
  • and of course, don't refactor everything on a whim. – gbjbaanb Dec 28 '19 at 23:23
1

Continuous Integration.

To understand why CI helps consider that in order to experience a merge conflict the change(s) in that branch need to conflict with one or more changes on master:

             A  B  C
master    *--*--*--*--*
           \         /
feature-x   *--------

The longer the branch exists for the more changes will be made on master, and therefore the higher the chance that one of those changes will cause a conflict (even if the change on the branch is very small):

             A  B  C  D  E  F  G  H
master    *--*--*--*--*--*--*--*--*--*
           \                        /
feature-x   *-----------------------

There are various things we can do to lower the chance of any 2 changes conflicting (like avoiding reformatting, or carefully organizing work to avoid multiple developers working on the same area), but the simplest approach is to just merge in changes more quickly, reducing the number of changes made on master, and in turn the chance of a conflict:

             A     B  C
master    *--*--*--*--*--*
           \   /    \   /
feature-x   *--      *--

If you are practicing CI properly then everyone should be committing to a shared branch multiple times per day, i.e. once every few hours. That's quick enough that a lot of the time there won't be any changes on master, let alone conflicting changes. Whats even better is that even if you do see a conflict your changes will be small and so resolving the conflict should be relatively straightforward - in the worst case scenario (where you need to completely discard and re-implement your change) you have lost at most a couple of hours work.


In the comments its been suggested that developers should regularly merge from master instead of to master, which definitely helps, but not as much as continuous integration. For example in the following graph each commit on master (A, B and C) has a chance to conflict with any of the commits on the branch (X, Y or Z), for a total of 9 potential conflicts. Merging once into master at the end means all 9 potential conflicts need to be resolved at the same time:

             A  B  C
master    *--*--*--*--*
           \         /
feature     *--*--*--
            X  Y  Z

If instead we merge from master into our feature branch after each change on master:

             A     B     C
master    *--*-----*-----*---*
           \  \     \     \ /
feature     *--*--*--*--*--*
            X     Y     Z

Then each time we merge we need to deal with the following conflicts:

  • In the first merge we must resolve any conflicts between commits A and X
  • In the second merge we resolve any conflicts between B and X, B and Y
  • In the last merge we resolve any conflicts between C and X, C and Y, C and Z

Note though that we never needed to resolve conflicts between A and Y, because change A was merged into our feature branch before change Y was made. In total we managed to avoid 3 out of the 9 potential merge conflicts by regularly merging from master.

Also note that each time we merged the number of potential conflicts increased - the longer the feature branch exists the more changes (and therefore potential conflicts) will be made on master, but the real killer here is that each change we make on the feature branch has a multiplicative effect on the number of potential conflicts each time we merge.

Now lets consider what might have happened if we were practicing CI:

             A     B     C
master    *--*--*--*--*--*--*
           \   / \   / \   /
feature     *--   *--   *--
            X     Y     Z

This time we had to deal with the following merge conflicts:

  • When merging change X into master, we had to resolve any conflicts between A and X
  • When merging change Y, we had to resolve any conflicts between X and V
  • When merging Z, we had to resolve any conflicts between Z and C

As before, we never needed to resolve any conflicts between A and Y, as change A was already merged into our feature branch before we made change Y, but this time we also didn't need to merge changes X and B, X and C, or Y and C, avoiding 6 out of the 9 potential conflicts.

See this question for more information / guidance on regular merging into master:

Is it better to merge “often” or only after completion do a big merge of feature branches?

| improve this answer | |
  • 7
    This is baaaaad. Do not merge feature X until feature X is complete. What a mess this approach will make. Once very few hours?! Noooooope. Merge to master when you're done, not a minute before. – Lightness Races in Orbit Dec 28 '19 at 23:36
  • 5
    In my experience the only part of CI that's actually generally practised in the wild is the automated build server running build tests on each commit (or at least on a regular basis). Members of a good dev team regularly merge from master instead, thus solving the problem you're trying to avoid. – Lightness Races in Orbit Dec 28 '19 at 23:37
  • 1
    A feature should pass testing before being merged to the master branch. It should be impossible for an incomplete feature to pass testing. The last thing you want is your master branch full of incomplete features. – CJ Dennis Dec 29 '19 at 8:44
  • 1
    @LightnessRaceswithMonica merging from master into feature branches helps a little, but nowhere near as much as CI does - see my edit. – Justin Dec 29 '19 at 11:20
  • 1
    I'm not necessarily saying that CI doesn't avoid more merge conflicts (although I'm not at all convinced by that; a team of ten committing their works-in-progress every few hours is bound to be a horrible experience for all involved) - even if it were better for conflicts, it would still be tremendously disadvantageous overall. There is more to development than resolving merge conflicts. I've been speccing out a big new feature for a month or so, iteratively refactoring it as I learn more about my own solution. Should I have committed it piecemeal every few hours? It'd be in production. Ew! – Lightness Races in Orbit Dec 29 '19 at 14:45
0

"Programmers make mistakes all the time" --- John Carmack

People who make assertions akin to "I'm a 'rockstar' developer" or "Good coders don't have to deal with merge conflicts..." are more often than not posers who seem to be plentiful in the general software development community and should be summarily ignored as such.

Building software that will be in production used by actual people aside from yourself is not a trivial matter. You will make mistakes....and the people you are working with will often make mistakes. Basing an assessment of anyone's engineering competency on whether or not they have to deal with merge conflicts is nonsense on its face.

| improve this answer | |
  • 5
    I wouldn't consider (every) merge conflict to be due to a mistake, as this answer seems to imply. Sometimes different people need to make changes to the same part of the code and sometimes the most efficient way to do this involves creating a merge conflict in the process. But of course there are ways to make them less common, as discussed in other answers (and it may be considered a mistake to not take at least some of those steps). – Bernhard Barker Dec 28 '19 at 17:17
  • 1
    No merge conflict should be a "mistake". – Lightness Races in Orbit Dec 29 '19 at 0:39
  • You can actually be a certified Rockstar developer now (real start at 10 min 17 secs). – Peter Mortensen Dec 29 '19 at 17:23
  • There are already about 10 answers with Rockstar on Code Golf (though the code golf (trying to minimise the source character count) sort of ruins the intent with Rockstar). – Peter Mortensen Dec 29 '19 at 20:58
0

Whoever said that has a particular notion of "good coder" in mind, which probably goes like this:

  • The Good Coder works alone on a Great Program that he or she designed alone.
  • There are no forks of the program, or release branches or anything of the sort, and so there is never any rebasing activity that creates conflicts.
  • When bugs are found in old releases of the program, the users either dutifully upgrade to the current baseline, or else downstream package maintainers back-port the required fixes from the current baseline (thus Good Coder never sees the conflicts that are involved).
  • In the rare event that some outsider provides a fix or enhancement for the Great Program, they must supply a patch that applies cleanly to the current development head, so the Good Coder is shielded from any forward-porting effort involving conflict resolution.
  • Lastly, Good Coder never rearranges the order of his or her commits (such as with git's interactive rebase).
| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.