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.
- We cut our two
release
branches:A 1.0.0.001
(which yieldsA 1.0.0.001
andA Proxy 1.0.0.001
) andB 1.0.0.001
(which yieldsB 1.0.0.001
). - Both projects get their version number advanced one minor version number:
A 1.1.0.002
andB 1.1.0.002
- We update NuGet packages for Project B:
B 1.1.0.003
, now consumingA 1.1.0.002
- Testing is completed, no bugs were found, final signoff is approved. Both project are merged to
master
:A 1.1.0.003
andB 1.1.0.004
(consumingA 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.