The main problem you are going to face is that when you are combining the feature branches to a release branch, you'll need to solve all the inter-branch conflicts. Merge conflicts are the easier ones, since they pop when you are merging specific branches and you can ask the branch owner to solve them(it's far from ideal though, since the branch is not fresh in the owner's memory). But not all conflicts pop us as merge conflicts - some create compilation errors or runtime bugs, and it's not as trivial to figure which feature branches have caused those.
A possible solution can be to shorten the release cycle - adding more rapid "sub-releases", e.g. twice a week. This will limit the number of feature branches you are merging on each sub-release, which in turn limit the conflict potential. This, of course, comes with it's own problems - a frequent release overhead, where the release master needs to choose which features to merge in each sub-release, and after the sub-release the developers need to merge/rebase their pending feature branches(and resolve conflicts).
At any rate, I think your fear of branching-from-develop
is unjustified. You are portraying develop
as some big playground where all developers push their unfinished scrabbles of untested code - and it's not true. The feature branches fulfill this role. develop
, while it might not need to be as stable and as rigorously tested as master
, should still have a certain level of stability - the primary rule is not to push to develop if it'll prevent the other developers from continuing to develop even if they merge/rebase develop
to their feature branches.
This essentially means that you don't merge a feature branch to develop
unless it passes automated tests(doesn't have to be the full suite - if you have a 10-minute suite that catches most bugs and a 5-hour suite that catches even the rarest of bugs, test the feature branches with the 10-minutes suite), so it should be OK to merge it to develop
.
Note that master
still needs to pass the 5-hour suite, and you have no guarantee a a merged feature branch won't break the 5-hour suite - but neither does your model provide such guarantee. The point is that even if a merged feature branch does break the 5-hour suite - it's still a branch you want in the next release(otherwise you wouldn't have merged it to develop
), and the solution is rarely to exclude the feature from the next release.
Update
To answer the asker's first comment to this answer:
When runtime integration bugs arise, the affected feature-set team will be assigned to correct it. If it is caused by code from features created by any of the other teams, fixes are made into pull requests to the offending feature branch. Pull requests are then reviewed by the team that owns that feature, merged in and then merged into the release package. The team that knows how a feature should work makes the fix, the team who owns the offending code reviews it.
This method of solving bugs has several drawbacks compared to solving them as part of the preparation of a feature branch to be merged into develop
:
The feature-set where the bug happens is usually easy in to figure in both methods. The actual changes that invoked the bug are trivial when branching from develop
and very tricky when branching from master
. The former only gives you a cue about you about who should be assigned to try solving the bug first, which is not as useful as the actual lead you get from the letter. At any rate, branching from develop
allows you to have both hints.
The responsibility is backwards. If anything, the owner of the offending code is the one who should fix it, since they know best what they are trying to achieve, and the owner of the feature-set is the one who should review it, because they know best how the different parts of the feature-set should interact with each other.
But the branch-from-develop
approach has an even better way to decide who will be the one to start solving the conflict - it's the one who tries to merge last!
Now, that claim might seem a little weird and arbitrary - it looks unfair to "punish" the developer who pushed last for being slow. But I believe they are the best choice for starting to solve the problem:
They are already in the context of the problem. This is the most important reason - being in context is crucial for solving problems, and entering context is hard. But the developer who pushes last is already in context, because that's the task they are working on. They have already build the mental model that can help them solve this problem.
They are available. They don't have something more urgent to do right now, because what they were doing was trying to merge their feature branch, and solving the conflict is required for merging the feature branch.
They don't have to actually solve the conflict entirely by themselves - just to be the first ones to look at it. When examining the problem they can decide some other developers need to be involved. Since they are in context, they are in the best position to tell who these other developers are. Also since they are in context, they can help bringing these other developers quickly into context.
That pull request into the offending feature branch will be a nightmare. The code in the feature branch works, because the other branch it was conflicting with is not part of it. So, you are sending a fix to a problem that's not yet there, that might have to relies on changes that come with the same code that introduced that problem. There is no sane way to do that without merging/rebasing the other branch(or the new release) into the feature branch - but if you do that you are just using branch-from-develop
with develop
having it's name replaced on each release.
In Gitflow Workflow, an hotfix is a branch that is created from master and in the end is merged back in master AND in develop.
The general (and well known) idea behind the "don't directly commit on master", is to have in master, a state of your code that is completely safe to deploy in production.
However, it can serves other means. For Github, the master also serves as a tracking tool to know when a feature is put in production. If you commit on another branch and then fast-forward master on it, you will lose the merge commit, and by extension, you'll just put a lot of tiny commits in your master timeline, losing the ability to revert a whole feature by simply revert the previous merge.
Another problem with this approach, is to know WHEN you can release/deploy. If everyone commit on master, you will have to synchronize all the development to have a coherent state of your code. For monolithic releases with a clear scope, it can be ok, but in my case, as an integration developer, features come and go and you have never a single point in time when "all features are done".
However, not commit in master does not mean having a branch master and a develop. As pointed in the Github presentation, if you do a feature branch workflow, you can work only with a master, creating a branch for each feature, and merges them back in master (without fast-forward) at the end.
In my humble opinion, Git Flow (master/develop + features/hotfix/release branches) is more adapted in a context where you don't really know when you'll have to release it (not at each feature done), and when the last release is often the one to hotfix. A gitflow chart can be convoluted, while a feature branching workflow is often more simple to read. The "noise" is often due to release/hotfix commit in what will be deleted branch some minutes after.
Example for Gitflow
Example of graph for Feature Branching Workflow
Best Answer
Which Git branching model you should choose depends completely on the development process you want to use. The popular “git flow” you mentioned is concerned with products that have clear releases and largeish features that are developed independently. The development branch represents those features that should be part of the next release.
This opens two opportunities where QA can be performed: QA can either be done release-oriented or feature-oriented.
In a release-oriented QA process, a release branch is started and QA reviews that state. If QA finds problems, you can fix them on the release branch which is later merged back into the develop branch. This assumes that there won't be any huge problems that would result in a feature from being rejected completely, so you do need some testing long before.
A release-oriented QA process can work very well with semi-frequent but regular releases, e.g one release per month. The devs spend one month preparing the next release. Then QA has one month reviewing the release. During this time, any found issues are fixed. At the end of the month, the version can be released and QA gets the next version from the developers. This avoids idle time for both QA and developers, at the cost of lengthening the cycle time between inception of a feature and its release.
In a feature-oriented QA process, the feature gets reviewed when it is merged into the develop branch. So the state of the develop branch is always QA'd and release-ready. This spreads out risk over a longer time and makes it possible to reject an unready feature before it is included in a release, without also delaying unrelated features.
The drawback is that you can QA at most one feature at a time, or you might miss interference between different features. This could be a bottleneck of a process, and discourages small, easily reviewed features.
Some QA activities can also be performed before anything is merged. E.g. if a feature involves user interface changes, then focused UX tests can be performed in isolation for that feature long before it may be released. This requires that devs can ask for QA assistance when they think it's useful, without having to navigate any red tape. This also requires that QA is sufficiently flexible to accommodate these requests.
All of these strategies can and should be combined. The closer you get to a release the more important QA is, but earlier feedback is more cost-efficient because changes can be made more easily.
Note that automation and tooling can have huge benefits here. This speeds up and streamlines your process, which can have an exponential effect on productivity. This starts with simple build and deployment automation so that QA can easily run the software of any branch they would like to review. Test automation can free up QA to do more valuable high-level tasks, such as feature validation, smoke testing, manual exploratory testing, and developing new test scenarios. This isn't really specific to any process or branching scheme, but solid tooling can make it easier to involve QA earlier in the development process.