As part of our work at Leighton we are always striving to find the best practices to follow as part of our software development process. Often, this involves creating or formalising a new process. This post details the formalisation of a process we have used in our source control strategy which allows for large sets of changes to undergo small, exclusive peer reviews without posing a threat to the trunk branch.
We currently follow the Gitflow workflow. This works great for us in most circumstances but occasionally we run into a problem where a piece of work will require a lot of changes.
There are several ways to approach integrating a lot of changes, each with their own caveats:
- One Feature Branch with One Pull Request: This will keep the trunk branch safe from incomplete changes being merged in but will result in a large pull request which can only be undertaken by peer reviewers when all the work is complete. Large peer reviews aren’t ideal as problems can slip through in the large raft of changes that peer reviewers miss. It also will likely cause delays as peer reviewers will need to first wait for all the changes to be done, then will need a single large block of time ideally to sift through the changes.
- Many Feature Branches with One Pull Request: This allows for small pull requests with unique sets of changes which is greatly beneficial to the peer review’s time and quality. However, it poses a risk to the trunk in that unfinished changes will be merged in. The use of feature toggles could be introduced to mitigate this risk, but even with them present there could still be unforeseen consequences on other ongoing work in feature branches elsewhere.
- One Feature Branch with Many Pull Requests: This will allow for smaller pull requests which will serve the peer review timing and quality better. It will also preserve the safety of the trunk branch. However, it will results in one of two undesirable outcomes: [i] the pull requests will be left open resulting in the running of the tests and notifications being sent to all the peer reviewers on every subsequent change or [ii] the pull requests will need to be manually declined by the change author which incurs a risk of peer reviewers either wasting time re-review the same changes or missing changes in subsequent peer reviews.
How a Cascading Feature Branch Works
The alternative approaches show the same set of potential issues, either a threat to the integrity of the trunk or large, unwieldy pull requests. A new pattern for managing feature branches is needed to avoid these.
The Cascading Feature Branch functions by the work to be done being broken down into smaller tasks (as should be done already if following a decent refinement process) and put into an implementation order.
The first task should be the first branch. When the changes to complete the task are done a pull request with the trunk should be made but no merge made yet. The next branch should represent the next task and should branch from the first task’s branch. When all the necessary changes are made for the second task a pull request is made between the second and first feature branches. A third feature branch can now be made from the second feature branch for the third task and the process repeats like so for all tasks.
Eventually when the changes on the last branch for the last task are complete and its pull request with the second-to-last feature branch is approved then the merging can be begin. The merges occur in reverse order, so the last feature branch merges into the second-to-last feature branch. This branch then merges into the previous branch and so on until a final feature branch is left which can then be merged into the trunk. Essentially, the pull requests are merged in the reverse of the order that they were created in.
Note: If there is a change on the trunk it will need to be brought into all the branches. This means it needs to be merged into the first feature branch in the cascade from the trunk, then the first branch will need to be merged up into the second and so on until it reaches the last one.
This approach ensures small pull-requests with exclusive sets of changes in them which will help ensure a good quality peer review.
The use of multiple pull-requests occurring at different times will also help timing by not forcing peer reviewers to wait for all the changes to be made.
The fact all these changes are not merged into the trunk until they are all complete should also help protect the trunk and other concurrent pieces of development work.
This approach works well where the set of changes is finite. If the work is likely to have more on-going changes then an alternative approach, such as creating a branch to stand-in for the trunk and merging into that regularly would be better suited (if following this approach, cascading feature branches would still work and be beneficial -the only change would be that the trunk would be replaced by the stand-in branch).
This approach, though complex, appears to be worth using in any circumstance where a large body of finite changes can be broken down into smaller tasks to ensure better quality peer reviews without endangering the repository trunk.