It's been over three months since our company started running Git Flow. Here's a record of how our version-control workflow evolved as the team grew and the number of features and users increased.
This kind of workflow helps us deliver stably and continuously.
# One main branch
master
- As the Team Lead's personal project with hardly any users,
masterwas my playground — I changed whatever I wanted however I wanted. If I broke something, I'd just revert it before anyone noticed.
# Two main branches
masterdevelop
- As business requirements grew and the first team members joined, we started splitting branches into
developandmaster. Team members developed directly ondevelop.developandmasterwere each wired to the CI/CD of the staging and production environments respectively —developfor testing,masterfor delivery. Each development cycle,developwas merged intomasteronce as a new version release.
# Two main branches, one sub-branch
master(main)develop(main)feature(sub)
To ensure code quality, everyone had to open a
featurebranch offdevelopwhen implementing a requirement, and every merge back intodeveloprequired a Pull Request reviewed and merged by the Team Lead.To keep my own code easy to review, if a refactor was needed during development I'd open a separate
featurebranch just for the refactor, and new features would be built on top of that refactor branch. One feature could be split across several PRs, keeping the number of files changed in each PR as small as possible.With requirements piling up and the team growing again, to improve communication and stay in sync as the project expanded, everyone could Assign their PRs to relevant members (people who had touched this code before, or who were about to). The team's center became decentralized — everyone could comment on and Approve each other's code.
To keep coding style consistent, every fixed period we'd review the PRs raised during that period and document the coding style, so future hires could quickly understand the project and integrate into the team.
# Two main branches, two sub-branches
master(main)release(sub)develop(main)feature(sub)
- We used
developas the testing environment. When many people collaborate and unfinished code accidentally gets merged intodevelop, it disrupts the QA testers' work. So we added areleasebranch to take overdevelop's testing role, moving the original staging CI/CD fromdeveloptorelease. This way, merging a PR intodevelopno longer triggers a deploy — not only cutting unnecessary build time, but also, when a problem shows up on thereleasebranch, we can promptly rebasereleaseonto a known-good commit ondevelopso QA testing isn't affected. When testing hits a problem, fixes can be made onreleaseright away — just remember to merge it back into the maindevelopbranch in the end.
# Two main branches, three sub-branches (GitFlow)
master(main)hotfix(sub)release(sub)develop(main)feature(sub)
- Even after going through this whole flow, bugs still occasionally get deployed to production. When that happens, you need to open a
hotfixbranch offmasterfor an emergency fix, and after confirming it's fine, merge it back into both major branches,masteranddevelop. If you don't merge thehotfixintodevelop, the next cycle's deploy will very likely overwrite the fixed hotfix.
Do you have any reasons for not using Git Flow? Feel free to share in the comments below.
