Some years ago, a git workflow model called git-flow took the software development world by storm.
I'd always thought that the workflow was far too complex and I couldn't figure out what purpose it served. Now, after having used it for some months, I can just confirm my impression: git flow is too complex and has little practical use.
TL;DR: I have a single, precise objection to that: the heavy branching is counterproductive. It won't do any good to your developer workflow, merging multiple branches back to develop will lead to conflicts, and if you really want to do Continuous Integration with it, the setup will be complex.
What are all those branches for? And how do I integrate them in a CI pipeline? And if I don't integrate them, how do I test the changes they carry?
A small explanation: usual CI pipelines offer a set of deployment hosts that continuously integrate the changes from a branch: as an example, let's imagine we've got three components in a project, from three separate repositories:
- data processing
The frontend communicates with the backend, and there's a back-and-forth two-way communication between the data processing and the backend component.
There'll probably be a machine in your staging/development infrastructure called something like integration-master (master is the branch) where those three components are continually deployed and can be tested by anyone, constituting the current state of the art of your deployment (remember: works on my machine is not enough).
It's even possible that those components are kept physically separated; so you'll have integration machines named like integration-frontend-master, integration-backend-master, and so on; for each component and branch, you'll get a new host.
What should I do with git flow? Suppose I create a new backend-only feature, feature/antigravity, departing from the develop branch. Suppose that I can create a proper CI pipeline that builds and tests such feature. Where am I going to deploy such branch? Do i need to create a new hostname in the internal network on the fly? And what frontends and data processing machines should I use? Should I deploy new machines for the frontend and the data processing from the develop branch in order to let the whole system with such feature to be tested?
Yes, I could do that. But that doesn't mean it's a good idea. And surely it's not as simple to implement as a fixed branches model.
Or you could just skip doing the deployment for the feature branches, they'll be tested when they're merged back to develop. Great - now the feature branches are completely useless.
And if I have indeed many feature branches at the same time, won't I experience the same old integration nightmare that killed many a programmer once upon a time? Every merge back to the develop branch would carry a substantial risk of getting messy. Wasn't continuous integration meant to avoid that?
Let's see the alternatives.
git builtin workflows
git help workflows
shows many possible workflows; some include the so-called topic branches, which resemble git-flow feature branches; I don't especially like them, since they have similar issues.
A simpler workflow proposal
For a standard piece of software with linear development - i.e. a software which is got exactly one stable version and for which I don't need to do maintenance of old versions, a simpler model is (use branch names as you prefer):
- master : holds the current stable release; this is the version of the software which is currently pushed to production.
- rc : normally, this branch points at the very same commit as master; but if a bugfix to the production software is needed, it's done in this branch, it's tested, and then is quickly merged to master or rollbacked. rc should never diverge from master for long periods of times, it's not meant for that. When doing a new software release (which should happen often), develop is merged to rc, it's briefly tested with the production configuration, and then is merged to master for the final release.
- develop: where the actual development happens. Everybody commits and pushes to this branch. When the needed feature set for a release is ready, the branch gets merged to rc. Feature Toggles are employed as needed, so that even if a merge of an incomplete or untested feature goes to rc and then to master it's not enabled by default and can do no harm. Any bugfix merged to master must be merged to develop as well.
- experimental: this is optional and may not be always active; yes, sometimes there's a huge refactoring or there's an experimental feature which really requires a separate branch. But it's a last resort. It's usually created at a certain point from the develop branch, where it gets merged back if the experiment goes well.
This approach is quite simple and easy to understand, and should solve most development issues.
Here you are.
Update 28 Jul 2016
After some discussions with some people, I'd like to clarify one point: it is entirely possible that you sometimes want some long-lived, named branches. And that, in such context, you'll want to setup separate CI/CD machines for those branches.
What I reject is the a priori attitude towards branching that git-flow tries to pass as a good practice.