Beside ambiguous requirements, tight deadlines, and an unstable legacy codebase, merge conflict is another common fear that annoys and disrupts developers the most while creating software.
From the fact that Merge Conflicts might appear in this post stating that long-lived branches should be avoided as much as possible to mitigate the chance of conflict, here are some guidelines to help any software teams deal with this concern.
1. User Story-Based Task Description
Human brain is designed to consume stories, not ambiguity. In software development, a User Story is a short, simple description of a feature or requirement told from the perspective of the end user. It’s a fundamental element of Agile and Scrum methods — meant to capture what the user wants and why, without prescribing how developers should implement it. A user story usually follows this format: As a [type of user], when [something happens], the user wants [some goal] so that [some reason]. For example: As a registered user, I want to reset my password so that I can access my account if I forget it. This helps teams understand who needs something, what they need, and why it matters.
Depends on how big the goal user wants, a User Story can be split into simpler stories. We don’t have to write an essay in the task description because we are not at school. Clarity is the top priority when writing task descriptions, so don’t think, just tell stories.
When a User Story is simple enough, a task stemming from it can have a small scope of change with a limited effect on the codebase, and in a predictable way. A small scope of change in a task helps to mitigate the chance of conflicts. Even when conflicts occur, resolving them can be easier because they happen in fewer places.
2. Merge or Rebase Daily, Resolve Early
When tasks are defined well and the scope of change is limited, the branch can now be short-lived, which is acceptable for the Rebase tactic. Depending on the preference for the history commit tree, both Merge and Rebase are okay. The recommended practice here is to do this daily: merge the main branch into new branches or rebase new branches onto the main branch. This practice helps to raise early awareness of possible places that might cause conflicts, allowing us to adjust our coding tactics or sync up with other developers about the changes that are made.
3. FIFO Merging
It is obviously right to prioritize tasks, but priority should not dictate the order of merging code because it can turn some branches into long-lived ones when higher priority tasks keep being merged first. When a task is in progress, such as on your Kanban board, and then moves to the Done column, it should be merged as soon as possible regardless of its task priority. What is completed first should be merged first (First-In-First-Out order). To achieve this state, utilizing any tool to automate the process is highly recommended. Of course, the completion of a task here includes the testing phase as well.
When a task is well-defined with a limited scope in User Story format, and somehow it gets stuck and turns into a long-lived branch, we need to review its necessity:
- If we don’t need this feature anymore, we should discard the task and close the branch.
- If we still need this feature, but it contains risky changes, and that is why no one dares to merge it into the main branch, then it is time to create simpler stories from the risky parts. Don’t let the task be stuck. Keep feature branches short-lived.
4. Commit with Task ID
The commit message is encouraged to describe what changes are made, but there is nothing to enforce or ensure consistency. This depends on how well a developer can explain things. To make it simple and scalable, it is recommended to begin a commit message with a Task ID; for example: #1234 fix things. This way, whenever anyone wonders why a commit was added, they can trace back to the related task description, allowing the User Story to provide clarity.
5. Long-lived Branches as Microservices
For any reason that a branch is planned to be long-lived, such as when developing a challenging feature that inevitably requires a long time for development, consider turning this significant part into Microservices. Microservices architecture can keep new code in a separate repository, which in turn can mitigate the risk of conflicts with the existing main branch. The main system can communicate with these new Microservices in any way to get things done without worrying about large changes from new features. The new Microservices can have their own task board with their own User Stories, and here, the Main system is the User.
Time is money, so Don’t waste development time for resolving merge conflicts !
Build – Secure – Evolve with the-tech-lead.com
