* what is being tested by QA/Product Owner/clients etc right now? What do we do when they actually have feedback that needs fixing?
* how to keep your modifications/patches to SomeUpsTreamSoftware manageable?
* How to deal with parallel development by a team of four? Of twelve? Should all work halt until your PR is merged? What if there are conflicts? How to handle longer lived development?
All of these are hardly ever covered by "keep it simple". Turning that "keep it simple" model into a giant mess of rules, guidelines, scripts and whatnots. Basically a very poor version of Git flow.
Yes. Git flow is often overengineered for CD types of workflows. But it does have all the cases and situations covered that you will eventually run into, and that your homebuilt "simple" flow has not.
Edit: to be clear: there are solutions for all my points in your Simple Workflow, certainly. But here's how git-flow helps:
* release-branches are ideal for putting work in front of stakeholders before release. Allowing improvements on the release. Even in a CD workflow, having something that is separated from "mainline" during the release-window is a good idea. A release-branch is just that. having a separate mainline where during that time new releases can be made is smart too: you don't want to be forced to wait for feature X to land before you can roll out some important fix. I.e. release, master and hotfix branches.
* support- branches in addition to feature branches allow easy rebasing when upstream changes and you need time to re-apply your patches and customisations. You can e.g. branch off a "support-3.1.4" off the 3.1.4 release from upstream and apply your feature/awesome-hack-x on that.
* feature branches are ideal for parallel development. Being able to merge into a branch that does not immediately release (separated from mainline) is smart, because it allows multiple team members to merge to and then make a release. i.e. "development" and "feature"
Another one that I haven't seen mentioned yet is when your software has to be deployed to multiple targets, and each needs the same foundation but also some target-specific adjustments. Maybe you're going to run on different OSes and need to use a different UI on each OS. Maybe you're writing firmware for a range of similar products and each product has its own quirks. Maybe you need to change some code on platform X because there's a known compiler bug where the normal code doesn't work. There are plenty of practical circumstances where this sort of thing can happen. You want to control and ideally automate as much of it as you can, for the same reasons as any other deployment process, and more complicated but systematic branching strategies are often useful for that.
If you need to build for multiple targets or have different special versions, than write your build scripts to produce multiple versions. The Linux kernel builds for lots of targets, but there is only one master tree for it.
Your Linux kernel example might be quite apt here. While the authoritative version may have a single master, how many distros maintain their own fork of the kernel and probably numerous other packages, merging in changes from upstream regularly, but only ever making releases of their distro from their own forks where perhaps they have their own bug fixes or other adjustments applied?
I have a horror of this due to having experienced a strategy where the team took a new (subversion) branch every time they started a new project (i.e. were building a new device in the same family). You had multiple branches with divergent development but lots of common functionality. If you wanted to fix a bug common to all the systems you had to fix it 4 times.
I absolutely believe that there are better ways of implementing that approach than they used, but I also am convinced through experience that you should have one single line of development and everything else is just 'views' on that (e.g. point release branch is just a view master that is frozen, plus some filtered recent history).
To take your Linux kernel example - the various forks (I'm working with the Xilinx Petalinux Kernel now) don't really fit into the normal model. They're a combination of a release branch and a feature branch - they take the Linux Kernel at a certain time, then add some patches on top. Those patches then usually get submitted (indirectly) back to Linus so that they join the master path of development. That means that other people benefit from them, and refactor them when appropriate.
Yes. That is exactly what you do.
How do you know which ones are going to break the build, how do you know which ones just aren't going to merge due to divergence?
You use tools like unit testing and continuous integration running on all of your key branches, just like you would on a single-master project.
I have a horror of this due to having experienced a strategy where the team took a new (subversion) branch every time they started a new project
That brings back painful memories. I think it's fair to say that in the area of branch management, modern VCSes are much easier to use than Subversion. Even if you're not using them as physically distributed, the same tools for readily transferring work across branches and tracking what has gone where are also useful in other scenarios such as the one we're talking about here.
I also am convinced through experience that you should have one single line of development
I guess this is just a case of everyone's experience being different. My experience has been that projects that try to keep everything on a single branch when the reality is that you will be making several significantly different types of build over the long term can become prohibitively expensive to manage. You don't necessarily have to use a multiple branch strategy to deal with that, or that strategy alone; I mentioned a couple of other useful options earlier on. But trying to use a single master and then make multiple types of build regularly using just informal processes or ad-hoc changes on release branches is a recipe for human error IME. Something has to systematically track and control the divergence or things get really messy.
They're a combination of a release branch and a feature branch - they take the Linux Kernel at a certain time, then add some patches on top. Those patches then usually get submitted (indirectly) back to Linus so that they join the master path of development.
This is exactly what typically happens in the situation I was describing as well.
You would presumably have some root with the common code, and then you maintain your per-target branches off that. Most development work still gets pushed to the common root as usual, and then all targets benefit unless they're specifically overriding the default code in that area (which is handled at the point of merging into the target-specific branch).
Any changes first made on target-specific branches can also be passed up and then back down onto other targets if it turns out that they are more widely useful, or transferred directly to one or more of the other target-specific branches but without applying the change to the common root. However, typically you would do anything like this via some sort of cherry-picking process; you would never normally transfer all of your changes back up by merging a target-specific branch back into the shared root.
There are better ways to manage that.
I'm following the process they described on a team of about 40 developers, and it actually greatly reduces those problems by forcing people to simply not to have long lived branches. Git flow just puts off the pain of working with that many people until later and creates a giant train-wreck when you finally try to incorporate your feature branch.
If you have a large feature, then use feature flags to let you release it to production dark before it finishes. If you want to do a large refactor, either find a way to do it incrementally or keep both the old and new versions in place together until the new version is completely done.
> release-branches are ideal for putting work in front of stakeholders before release
Feature flags are better for this. If you don't want users to see an incomplete feature, that doesn't mean they have to be using a version of the software that doesn't support that feature. You can just explicitly hide the feature from certain classes of users.
- There is only one released version in flight at any time
- You don't need to do a lot of validation before release. E.g. if you release on physical media, or need certification, or shoot the program into space and can't patch it.
Basically, it works for web software where you want (or can have) continuous deployment.
For any scenario that doesn't do "continuous deployment", I think having multiple release/maintenance branches is inevitable. In that case, master is the vNext development branch, and each major version will have a branch for stabilization/validation point releases.