7

We are trying to work through an effective branching strategy for our organization so releases, versioning and such are all handled consistently.

We have many C#-based solutions, each with anywhere from 2-60 projects. Each solution relates to a particular product for which we release updates on a regular basis. These products have some interdependency, but not much. When they do cross over, we have traditionally created a proxy-type project that provides the logic to access the resource to the dependant projects.

For example, we have a Web API website which has a proxy project to abstract the endpoints (spinning up a WebClient, authenticating and making the call to the URL) so other projects just need to call that proxy. These proxy projects share a version number with the project they relate to and are released/consumed via an internal NuGet feed. We generally follow SemVer to help manage any breaking changes, with appropriate version checking on the proxied project (eg the Web API) to ensure behavior works as expected.

This strategy has worked well for us, but there seems to be one downfall we can't overcome - the release cycle.

According to Git Flow, we cut release branch from our primary develop branch for each solution (we release all of our products in tandem at the same time). Within this branch, we increment the version number (major/minor depending on the types of changes) and update internal NuGet packages to the latest version. These release branches feed into our final QA environment, which is where all the products are tested together for emergent bugs and a final signoff occurs.

Technically, we should be able to finalize all of the release branches and deploy the resulting code from each master branch. However, a version mismatch between deployed and consumed assemblies would occur.

An example for clarity:

We have Project A, which includes a Project A Proxy and has no dependencies, and Project B, which consumes the Project A Proxy.

I'm going to assume every commit, regardless of the branch, is built with a unique version number per project, and we deploy the assemblies that are build from the master branch.

  1. We cut our two release branches: A 1.0.0.001 (which yields A 1.0.0.001 and A Proxy 1.0.0.001) and B 1.0.0.001 (which yields B 1.0.0.001).
  2. Both projects get their version number advanced one minor version number: A 1.1.0.002 and B 1.1.0.002
  3. We update NuGet packages for Project B: B 1.1.0.003, now consuming A 1.1.0.002
  4. Testing is completed, no bugs were found, final signoff is approved. Both project are merged to master: A 1.1.0.003 and B 1.1.0.004 (consuming A 1.1.0.002)

However, now there is a mismatch in version numbers - we deployed A 1.1.0.003, but are consuming A 1.1.0.002. In this case, we might be able to assume that these contain the exact same code and that the difference in version numbers is due to a branching merge, which is true here, but may not always be.

In the past, the way we worked around this was to sequentially merge the release branches in order of the dependency chain and update the dependent projects, but if a bug is found upstream in the release cycle, we have no way of patching it outside of a hotfix branch (which doesn't seem to make sense because the code is technically not deployed yet - it's still in testing because of the dependant products, and has not been completely signed off yet).

How can we resolve this paradox? We must keep all our product release cycles in sync because of the dependent relationship between them, but we also want to ensure that for each product and dependency the versions that have been tested and signed off on are the ones that were released.

2

I don't quite understand how you suddenly got to version B 1.1.0.003, however I can't help but feel you are making this much more complicated than it needs to be. You said yourself that all of your projects get released at one time, and that is likely because of the spider web of dependencies that projects seem to have with one another. A single change for a hotfix in Project A sounds like it can have a downstream effect where Project B likely needs to update its version dependency on Project A to the new hotfix version, causing Project B to have a new hotfix version, and etc...

Develop Branch

The way to think about the develop branch of any one project is for ongoing development towards the next major or minor version of that software component. Only after this has been thoroughly tested and ready for final QA testing will you ever need to commit to a new release number.

Release Branch

When you have committed to a new Release number, and everything is good to go, you increment your major or minor version numbers and update your dependencies. Its finally official, these components are going into production. You can always rebase your dev branch later and now your dev branch has the latest version number in production, and the latest bug fixes that were resolved in the Release branch.

Hotfix Branch

Basically same as release except you change the update or patch number in your software component versions.

Nuget Build Numbers to the Rescue

Official version numbers should ONLY be incremented or changed for stable "complete" code. All other code is considered in development so when managing changes in dependencies currently under development you should use tools in Nuget to reference dependencies on versions + build numbers. Build numbers are inherently temporary and can be lobbed off before you merge Release back into Master. I understand there are other tools that do this even better like Paket.

  • 1
    In this case, the B 1.1.0.003 comes from the merge commit of release to master; in our case the final number of the version is an auto-incremented number each time the build server runs. However, I now realize that the assumptions I made prior to the example are largely incompatible with each other, and we should address our versioning scheme to be more in line with what you described in your last paragraph. – kingofzeal Apr 5 '17 at 18:45
  • @kingofzeal I think that is a good idea. A build number is useful for development and testing, however you should version your stable releases for major, minor and patch updates absent of build numbers. Merging release to master absolutely causes a new unique build, however it is still the same release. – maple_shaft Apr 6 '17 at 14:55
1

I do not know how exactly you create the version numbers. Is a new number created also when a merge could be a fast forward merge? If not then I see a chance to solve your problem by making the merge in step 4. a merge that could be a fast forward merge. In fact this merge is never a fast forward merge because the Git Flow uses

git merge --no-ff release   

when merging into master branch.

I think the main point is whether there is something in the master branch what can cause the merge in step 4. could not be fast forward. If this merge could not be a fast forward merge then you will end with something different in master than you tested in release branch. If you follow the Git flow correctly the difference is only in the order of applied changes. (In the time of first hotfix you can have already several commits in develop branch.)

You can merge master into release branch once it is created and after every hotfix pushed to the master. This will ensure the merge in step 4, could be a fast forward merge.

Last thing to arrange is to adjust the versioning in such way it will not produce new version numbers for merges that could be fast forward. I do not have experience with this. I'm just thinking about your problem. It may be I missed something important.

1

We've used atomic change flow synchronized on multiple repos with success on similar situation in the past.

The basic idea is that you keep atomic changes in their own branch as long as you can. That you alleviate synchronization complexity and merge hell by using intermediary "release assembly" branches, development (or similar subsidiary branches) can be ditched at any point, and you merge back to master only when deployed to prod.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.