Table of Contents
- Prefix branches with the primary developer's ID
- Pick a branch organization strategy
- Clean up unused branches
- Lock the main branch
- Use a CODEOWNERS file
- Use CI/CD tools like Github Actions
- Squash PRs into one commit
- Use Conventional Commits
- Keep a CHANGELOG
- Remember that you are working with people
- Conclusion
I recently was asked by a friend about implementing "standards for branching, pull requests, etc. for working with a large group of developers". I decided to write up a post about it in case anyone else asks me about this question as well. Of course, this is just my thoughts and opinions, and I recognize that there are many different ways of doing a git workflow. Hope you enjoy!
In terms of advice, here's what I've found is useful in my experience.
Prefix branches with the primary developer's ID
The first thing that I'd do is make sure that every branch is prefixed by the primary developer's ID followed by a /. The ID can be anything from the user's initials to their github username to just their name, it just needs to be unique. Typically, I name any of my branches like bb/foo and bb/bar-baz. So if I'm working on fixing a parser error, I'd name my branch bb/parser-error-fix. If there are multiple people working on a feature, you can prefix the branch with feat/. The only branch that doesn't need a prefix is main (or dev or whatever other "permanent" branches you have).
Pick a branch organization strategy
In terms of how to organize the branches, there are many ways of doing it. This article by Atlassian describes the Centralized Workflow, and it has links to other workflows, including the Feature Branching Workflow, the Gitflow Workflow, and the Forking Workflow.
Clean up unused branches
Clean up unused branches. It helps to be clear about what work is going on if all the branches are active. If you need to keep a branch around that isn't being worked on, prefix it with archive/ or something like that. This also means that, once a PR has been merged, that branch should be deleted unless further work is being done on that feature.
Lock the main branch
Lock the main branch on Github/Gitlab/etc. The only way to get code into the main branch should be through a PR. This should be the case regardless of who is doing a PR. No one gets special privileges, since they can be abused. And it doesn't matter how small the change is. Even fixing a typo in the README should be a PR. You may also want to do this for other "permanent" branches that need to be quality-controlled.
Use a CODEOWNERS file
The reason for this is that you can require every PR to have a review before it can be merged. This allows all code in main to be quality-checked. And you can use a CODEOWNERS file (https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) to automate who should be reviewing a PR.
Use CI/CD tools like Github Actions
Another reason for requiring a PR is that you can then run Github Actions (or the equivalent tool on other platforms) on every PR. Examples from a project that I worked on are rust-pr.yml, which runs on every PR, and rust.yml, which runs on every PR and on every push.
In these Github Actions, we run all of the unit tests, fuzz tests, linting checks, and formatting checks, and we check that there are no unused dependencies. If any of these checks fail, then it shows up on the PR. This helps to establish a "standard" set of development practices that everyone is expected to follow.
Squash PRs into one commit
A third reason to require PRs is that you can then squash all of the commits from that PR into one commit. This helps to keep a clean history on the main branch that is more of a high-level documentation of feature additions, rather than a low-level list of all of the changes.
Squashing commits may not be right for you. Some people like to keep all the commits around, and that's okay. Sometimes I squash commits, sometimes I rebase all the commits, it just depends on what I decide is right for the project.
Use Conventional Commits
I've found that Conventional Commits tend to help me organize my work. The link explains them more in detail. They help me to define exactly what I'm doing, and they can also be used to generate a CHANGELOG (Conventional Changelog for example).
Keep a CHANGELOG
And that's another thing: keep a CHANGELOG file, if possible. There are some good resource out there for how to do it (like Keep a Changelog). It helps you to keep track of what is being implemented. The link above recommends a hand-written changelog, but I think that if you're working with a large group, you may not have the bandwidth or stamina to maintain that, so generating it might be the way to go.
Remember that you are working with people
Finally, remember that you are working with people. People have ideas about how they like to do things, and they don't like change.
Collaborate, don't enforce
Don't make it your job to "enforce" whatever rules you pick for them. Instead, collaborate with them to find what rules work for everyone. You won't be able to please everyone, but you may be able to find some compromises, and everyone will feel heard.
Make gradual and incremental changes
If you can, it will also help if you take it slow, introducing one change at a time. If you move too quickly, people might feel like they are having the rug pulled out from under them. Introduce changes gradually and incrementally.
Ask for feedback
And get feedback from the team while you do. Ask them how the changes are influencing them. What is working, and what isn't? Sometimes, I've found that ideas that work well in theory don't end up working as well in practice. So adapt and improve as you go.
Document standards and practices
And write documentation for the standards and practices that you work out. If there's not a place to read about them, then it's going to be a lot harder for people to pick up what is considered "standard" practice.
Conclusion
When developing a git workflow, you should ultimately tailor the workflow to the needs of your team. There are some useful tools available, including branches, PRs, and CI/CD. But it's important to remember that these tools should be serving the people using them.