C# – How to use Git Flow with multiple dependent projects

cgitflowrelease-managementsemantic-versioning

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 dependent 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 (e.g., 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 dependent 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.

Best Answer

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.

Related Topic