This is the 8th part of a series about 67 Bricks’s coding principles. The previous posts are: 1, 2, 3, 4, 5, 6 and 7.
The principle
Favour using small, short-lived feature branches that are merged back into the main branch after a short time.
The specifics of your git strategy – whether you use git flow or some other pattern, whether you prefer merge or rebase – are preferences that you should work out with your team. Across 67 Bricks we have a variety of opinions and encourage teams to choose options that work for them.
However, where we do encourage a particular style is in the general approach to how work is broken down into merge/pull requests.
Keeping branches small and short-lived makes for easier code reviews, and makes merge conflicts and other version control tangles less likely. The longer a branch lives, the more it will drift from the main branch, leading to merge conflicts and lost context as well as potentially duplicated effort and even broken functionality that only shows up after merging.
Smaller changes are easier to review thoroughly. They require less cognitive load and have less surface area for subtle bugs to hide. Reviewers can focus on understanding the intent and the implementation, rather than piecing together a sprawling set of unrelated changes to figure out what’s going on at all.
Short-lived branches reduce the risk of painful merge conflicts. The longer a branch lives, the more likely the surrounding code has changed on main in the meantime. With good habits, even when conflicts do happen, they are easier to resolve because the changes are limited and likely to be fresher in your mind.
This means every pull request doesn’t necessarily have to correspond to a fully functioning user-facing feature or Jira ticket. It’s perfectly fine to merge incremental changes. They should be complete for what they are, but that could just be a refactor or the addition of some well-tested functions that will play a part in your main implementation when it comes later. Everything should compile and build and all the tests should pass in every pull request too of course.
Inevitably sometimes you will have a larger, longer-lived branch. This isn’t necessarily a bad thing. Even when you aim for smaller branches, it will definitely happen from time to time. However when it does, ensure you keep it up to date with the main branch often – by merging or rebasing – to minimise the pain of the merge at the end.
When creating a new feature branch, always branch from the tip of the main branch unless there’s a specific reason not to. Sometimes it’s necessary to keep working from a branch that isn’t yet merged. In that case using stacked PRs can be a good way to maintain small, modular pull requests rather than increasing the size of the initial one or being blocked while you wait for a review.
As ever, there’s a balance to strike here. Code reviews require context switching by your colleagues, so having too many merge requests being opened can cause downsides of their own. Aim to find a sensible balance that errs on the side of smaller pull requests but also respects the time and attention of your colleagues.