With the school year officially over at most universities, we’re moving into one of the most exciting times of the year here at Avatria: welcoming the newest crop of recent grads into our ranks. Without question, there are approximately one million different preparations that need to be made when onboarding a junior employee to a development team, and there’s no way I could possibly write about all of them. Instead, I want to focus on one item that’s easy to overlook: creating a clear and effective code repository branching strategy.
While focusing on branching strategies with new employees may seem trivial, we’ve found that actually, the opposite is true. It’s rare for computer science programs to spend much, if any, time articulating the best practices in this realm. As a result, most new employees will have their own unique approach. Clearly delineating the finer details of process helps integrate new employees by allowing them to concentrate their mental load on larger issues.
But make no mistake: this isn’t just for the sake of new employees. Having an effective code repository branching strategy is key in ensuring that your developers can push code to production without stepping on each others’ toes. The larger the team, the more likely you are to have pieces of functionality touched by multiple developers, and the more likely you are to introduce merge conflicts as a result. A well-established branchings strategy will allow teams to greatly decrease merge conflicts, as well as have a plan in place to resolve any that do arise. These guidelines will ensure team friction remains low and energy can be spent developing features instead of fighting through complicated merges.
Here are some key strategies we recommend when establishing a team-wide branching strategy:
It’s almost impossible to have a solid branching strategy without properly tracking tasks. Using a platform such as Jira or ALM will help, but it’s also important to create a clear development hierarchy. At Avatria, we follow the classic Epics → Stories → Tasks design, breaking up each piece of functionality into bite-sized tasks. Our goal is that that each task should be:
Before work begins on an individual task, we recommend that a new branch be created for that task. This allows for easy naming conventions, and ensures that each branch only has one developer working on it. If you follow this method, a story with five tasks would have five separate branches. These tasks can then be merged into the story they fall under; if properly broken down, you can minimize merge conflicts between the branches.
Let’s say you have five teams working in parallel on the same website, all pulling from the same master branch. Inevitably, branches will deviate, so how do you resolve this? The answer is simple: communication, communication, communication.
Start by making sure that each team knows what the others are working on. A weekly status or code review call between team leads is a good place to start, but don’t forget to create a process where information flows downward. The developers closest to the code may be able to spot potential conflicts that development leads have overlooked. The more mindful developers are in their day-to-day work, the more likely they are to recognize areas of potential conflict to call out in these meetings.
Before work begins, figure out a plan to prevent opposing functionalities from impacting your own. Are there any dependencies, architecture choices, or other design considerations that can help avoid conflict? Consider how strategic development sequencing can aid the cause. If potential conflicts exist, it may actually be more efficient in the long run to work on secondary tasks while another team finishes up their work.
Finally, when it comes time to combine features, make sure that your own work has been fully tested independently of other functionality. Once you’ve documented or resolved the defects in your own code, then and only then can you merge with another feature. This is critical in ensuring that you’ve separated your own defects from those that are introduced in the merge, which will make troubleshooting and bug resolution much simpler and more efficient.
No matter how diligently you manage your branching strategy, merge conflicts are inevitable. However, there are ways to decrease the frequency of these merge conflicts with proper planning and a resolution plan. The best way to avoid complicated merges is to merge early and often. The more frequent the merges occur between teams, the smaller the changesets are, which makes for a smoother merge process throughout the development tracks of each project. An added benefit is that any larger technical design conflicts that arise between the teams are caught early on in development, which is obviously better than discovering these items at the end of the development cycle.
When a merge conflict does occur, identify which developers are responsible for the conflicting changes. Fortunately, since you’ve been following the recommendations above, this should be easy.
Bring the two developers together, preferably either an in-person meeting or via screen share to go through the code. Hopefully, the two will be able to find a resolution on their own. If not, they can escalate the problem to technical architecture team members.
In the worst case scenario, where a merge conflict will require a functional decision to resolve, escalate the issue to business stakeholders to help find a solution. This should be the last resort.
Let’s say you have a feature that’s completed and ready to go on to the production path (we’ll call this branch feature/username/JIRA-1). Hopefully, you’re using a tool like Bitbucket that allows you to create a pull request so that other team members can review the code you’re proposing to accomplish task JIRA-1.
Create a new branch that will hold all items that will proceed with the next build to production, and point the pull request for the feature branch to it. This is the release branch. We recommend creating a consistent naming convention for your release branches ahead of time.
Once merged, the release branch should hold all features intended to be pushed to production. After that, it’s up to your QA and business teams to sign off on the functionality included in the release branch. The complexity of the features, size of the changeset, and areas of functionality modified should all be weighed when considering the length of time necessary for regression testing. It’s important for the technical leaders of each teams to provide the context of the changes to the QA team to properly identify the complexity and scope of regression testing. The more merges & features released together, the higher the likelihood of introducing regression defects, so keep that in mind.
When ready, you can create a tag off of the release branch to create a “snapshot” to move to production.
Don’t overlook the small stuff. Branching strategies are critical to clean, efficient, and bug-free development when multiple teams or developers are working within the same codebase. For best results, plan ahead to figure out the structure and process for managing your code repository. Without one, you raise the risk of duplicate work, nasty merge conflicts, and friction between teams. Set your team up for success by establishing a strategy that will help everyone succeed.