The Git branching model for mainframe development
As Git became the de facto version control system in today's IT world, new terminologies such as "repositories", "branches", and "merges" arose. By agreeing upon a central Git server to integrate and consolidate changes, development teams were able to collaborate more efficiently and effectively. Building upon the open-source vanilla Git implementation, popular Git providers including GitHub, GitLab, and Bitbucket have implemented additional workflow features to facilitate a secure and stable development process. These include features such as pull requests (sometimes referred to as "merge requests") to support coordination with Git in larger teams. The term "pull request" will be used throughout this page to designate the operation of reviewing and merging one branch into another.
Many mainframe development teams follow a release-based or iteration-based process to deliver incremental updates to a pre-defined production runtime.
Characteristics of trunk-based development with feature branches
As mentioned in the Introduction to working practices for teams using Git, our recommended approach scales very well to support the needs of a range of team sizes, frequency and size of changes, and degrees of concurrent working.
Starting simple
The trunk-based development approach1 with short-lived feature branches is a simple and structured workflow to implement, integrate, and deliver changes with an early integration process flow using a single long-living branch: main
. Developers work in isolation in feature branches to implement changes to the source code, and ideally test the changes in a specific environment. Each feature branch (sometimes referred to as a "topic branch") is dedicated to a specific developer task such as a feature or bug fix.
A similar workflow is also documented by Microsoft without giving it a name.2
The main
branch is the point of reference for the entire history of the mainline changes to the code base, and should be a protected branch. All changes should originate on a separate branch created to hold them until they are ready to be merged. Using a branch-and-merge approach is natural in a Git-based development workflow, and is very lightweight both in terms of resource consumption and developer experience.
All changes start on a dedicated branch
Although all Git-based DevOps services provide direct access to the server copy of the code repository, developers will typically use an integrated development environment (IDE) of their choice to work locally on a clone of the repository. Standard practices ensure developers' local clones are synchronized with the server copy (typically known as the "remote") through actions known as "pull" and "push". (For those unfamiliar with Git terminology, these terms are explained in finer detail in Source code management.)
In a small team where there is almost never more than one change in progress at a time, using a branch enables the developer to make the changes in a series of commits rather than as one atomic change as they edit source and save files. Of course, the "one-line" change might mean there genuinely is only one commit that needs merging, but the process is so natural and light-weight that it is not worth making that a special case.
Such branches are able to be built prior to merging - this can eliminate the possibility of the merge breaking the build of main
, thus reducing the risk in making changes.
Merging a branch
A branch holds all the commits for a change - be that a single commit for a one-liner or a sequence of commits as the developer refined the change while making it ready for review and merging into main
.
The request to merge a branch is made explicitly, but can be as formal or informal as needed by the team. Protection of main
can mean that only certain people can perform the merge, or that a review and approval of the change is required before merging it, or both.
The action of merging can either simply take all the commits from the branch and add them to main
, or that multiple commits in the branch can be "squashed" into one commit - the latter can keep the overall history on main
"cleaner" if that's important to the team.
main
should always build successfully, enabling the team to choose when to package and deploy.
Scaling up
The use of branches for concurrently planned activities scales extremely well for busier teams. Additionally, epic and release maintenance branches accommodate specific development workflows and allow the model to scale even further. The latter two branches exist for the duration of the epic or release maintenance and are short-living branches.
The implemented changes of the iteration are then delivered collectively as part of the next release. Each development team decides how long an iteration is. We advocate for working towards smaller, quicker release cycles, but this model can also be used with longer iterations. Due to business or technical reasons, the merging of features into the main
branch can also be delayed. Although this scenario is a discouraged practice, the recommendation in this case is to group such features into a specific epic branch, as described later.
This branching model uses Git tags to identify the various configurations/versions of the application, such as a release candidate or the version of the application repository that is deployed to production.
Depending on the type of change, the development workflow can vary. In the standard scenario, developers use the main
branch to deliver changes for the next planned release, while the release maintenance branches allow fixing of the current release running in the production runtime environment(s). Using epic branches is optional for development teams, but is a grouping mechanism for multiple features that should be built and tested together, thus allowing teams to increase the concurrency of working on multiple, larger development initiatives of the application. The epic branch also represents a way to manage the lifecycle of features that are not planned for the next planned release. In this way, it is a vehicle to delay merging the set of features into the main
branch for a later time.
The main
, epic, and release branches are assumed to be protected branches, meaning that no developer can directly push changes to these configurations. It requires developers to make changes on a feature branch and go through the pull request process. Before merging the feature branch into a shared branch (whether it is the main
branch or an epic branch), some evidence should be gathered to ensure quality and respect of the coding standards in the enterprise. Peer-reviewed code, a clean pipeline execution, and approvals are examples of such evidence, allowing the development team to confidently merge the feature branch into the target branch. In a continuous integration workflow, integrations are expected to happen early to avoid delaying merge conflicts or merges leading to an unstable build.
No environment branches
Notice that main
is the only long-running branch. In particular, there are no branches aligned to environments that may be deployed to. For example, there are no prod
, production
, QA
, or Test
branches.
This branching model exploits the ability to build a deployable package from any branch at any point in its history, and those packages can be deployed to your environments as needed to verify the changes they incorporate.
- Developers can build feature branches as and when they need to deploy to unit testing environments before they consider a pull request for completed work.
- The build of a branch that is the subject of a pull request can be deployed to environments with more extensive automated testing as part of evaluating whether the changes in the branch are good enough to be merged.
- The build of a release candidate level of code on
main
can be deployed to the full range of test environments to validate whether it meets the quality demanded of being deployed to production.
The build and deployment tools ensure clear
traceability from the point in the history of main
(or a feature/epic branch before merging) a package was produced. The deployment manager installs the package into the various testing environments and,
upon successful completion of testing and sign-off, into the production environment as a release.
The deployment manager maintains the inventories of the deployed packages for the various
runtime environments.
Additional long-lived branches aligned to more traditional environments will each
represent alternative histories and give rise to possible ambiguity as sequences of commits
that will need to be merged to multiple branches
rather than just to main
.
The single consolidated history on main
serializes the commits of merged feature or fix branches - and then is punctuated with explicit release
tags and/or release branches. Since a branch can be easily created from any previous commit, even if a
release candidate build needs a specific fix, this can be achieved whenever it is needed.
Naming conventions
Consistent branch naming conventions help indicate the context for the work that is performed. Throughout this document, the following naming patterns are used:
-
main
: The only long-living branch which is the only branch from which every release is initially derived -
release/rel-2.0.1
: The release maintenance branch for an example release named rel-2.0.1 -
epic/ai-fraud-detection
: An epic branch where "aiFraudDetection" is describing the initiative context (in this example, an initiative to adopt AI technology for fraud detection)
Feature branches also need to relate back to the change request (or issue) from the planning phase and their context. Some examples are shown in the following list:
-
feature/42-new-mortgage-calculation
for a planned feature for the next planned release. -
hotfix/rel-2.0.1/52-fix-mortgage-calculation
for a fix of the current production version that is running the rel-2.0.1 release. -
feature/ai-fraud-detection/54-introduce-ai-model-to-mortgage-calculation
for a contribution to the development initiative for adopting AI technology for fraud detection.
The above naming conventions help to easily identify the category of the branch you are looking at. The last segment in feature or hotfix branch names may include a prefix of the work item identifier.
Note: In the diagrams visualizing the workflows, the branch names are abbreviated to their contexts for readability. For example, for the feature branch feature/42-new-mortgage-calculation
, the diagrams will show feature_1
.
Integration branches
Specific branches, such as main
, epic, and release branches can be seen as integration branches, because their purpose is to integrate changes from other branches (typically feature branches). To drive the integration process of changes into a shared branch of code, mechanisms like pull requests are a convenient way as they guide the developers with a streamlined workflow. The number of integration branches required for your development process depends on the needs of the application team. However, while the cost of creating new branches is low, keeping them up-to-date, for instance by integrating release bugfixes from the stabilization phase into concurrent epic branches, can be expensive.
For application teams who want to embrace an agile development methodology and who sequentially deliver new releases with limited parallel development initiatives, they can use the main
branch and, optionally, the release maintenance branch as integration branches to implement the next planned release and potential bug fixes. The following diagram illustrates a branching model for a Git-based development process with sequential release deliveries.
A common, recommended practice is to squash the different commits created on the feature branch into a single new commit when merging, which keeps the Git history from becoming cluttered with intermediate work for the feature. This also helps to maintain a tidy history on the main
branch with only the important commits.
If the development teams need to work on a significant development initiative in parallel to the standard scenario of the next planned release, this model allows isolation using the epic branch workflow. The epic branch (epic_1
) in the following branching diagram represents an additional integration branch that is used to implement, build, and test multiple features that are planned for the development initiative and can be merged into the main
branch at a later time. The team decides which commit/tag of the codebase in the main
branch will be used as the base for the epic branch, although it is recommended to start from the last tag for the main
branch.
When the work items implemented on the epic branch are planned and ready to be delivered as part of the next planned release, the development team merges the epic branch into the main
branch.
Epic branches can be used to compose various styles of development processes. The documentation for Working practice variations provides additional samples.
Workflows in this branching model
This branching model facilitates three different types of development workflows. Git tags are used throughout each workflow to indicate and label important commits, such as the commit of a release that is built from the main
branch, or a maintenance release created from a release maintenance branch. You can learn more about the various tasks and activities performed by the development team in the context of each workflow on its respective documentation page:
- (Default development workflow) Deliver changes with the next planned release: With a single long-living branch, the development process allows developers to work and focus on the next planned release. After planning the work items for the next release, the development team is adding changes to the
main
branch by merging in pull requests for feature branches. You can also see demo videos illustrating this workflow at Git branching model in action. - Implement a fix for the current production state: This workflow enables development teams to resolve a production problem in the currently-released version of the application by leveraging a release branch that is used for maintenance purposes.
- Use an epic branch for a significant development initiative: Concurrent development activities for a significant solution development initiative that includes multiple planned work items for a later delivery (which could even be starting the development of a future release) are supported by creating an epic branch from a commit point in the history of
main
.
Learn more
This page describes our recommended Git branching model and workflows for mainframe development. This model is intended to be used as a template, and can be adjusted, scaled up, or scaled down according to the needs of the development team. Additional variations for the branching strategies and workflows can be found in Working practice variations.
For recommendations on designing and implementing the workflows described in this branching model, please refer to Implementing a pipeline for the branching model.
Footnotes
-
Trunk-based development approach: https://trunkbaseddevelopment.com/#scaled-trunk-based-development ↩
-
Git branching model documented by Microsoft: https://learn.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance?view=azure-devops ↩