Introduction
The goal of this post is to outline the key objectives we should reach regarding release management and explain why they are important.
Assumptions
We’re Using GIT
Anything else isn’t worth considering.
We’re Using an Issue Tracking Tool
We need something like JIRA, Trello, or Redmine that is integrated with our Source Control Management (SCM) tool.
We Want to Be Productive
No time should be wasted on tedious release preparations. Automation is key.
Version Numbers Have No Intrinsic Meaning
Version numbers are just identifiers. We use them to:
- Identify a set of features (major version).
- Track a bug fix (minor version).
Version Numbers Are Important
Version numbers must uniquely identify each build produced. They should correlate directly to a specific git commit hash, be tested by QA, and—if validated—be deployed to production without modification. Any defect, even minor, necessitates an increment in version number.
Feature Branches Are Essential
Feature branches allow developers to work on new features independently, fostering a smooth workflow.
Branching Strategy
The Dev Branch
We maintain a dev
branch where all developers merge their changes.
Feature and Fix Branches
Developers work from feature or fix branches. Branch names should be descriptive, for example:
(fix|feature)-${developer_name}-${ticket_name}-${extra_info}
When a developer completes work, the branch must pass automated tests, and peer reviews (at least two is ideal) before merging back into the dev
branch.
Note: Using feature branches doesn’t negate the utility of feature flags, and vice versa. You can still test your code in different configurations.
Release Branches
This is the critical part. When entering a new iteration or sprint (e.g., sprint 10), we create a release branch named release-v10
from the dev
branch.
After the branch is created, the version files (e.g., maven, gradle, npm, go resource files) should be updated to reflect the new version. While updating versions may seem cumbersome, it provides a clear picture of everyone’s progress and is particularly important when deploying.
Release Tags
Release tags are lightweight and can be created as needed, typically when QA is ready to validate a build.
Upon creating a tag, the version files should also be updated to the upcoming version.
Assuming the dev
branch is in a stable state, after creating a release branch (release-v10
), you should immediately tag it (v10.0
).
Production Branch
Also known as the master
branch.
In principle, we don’t need to worry about the production branch—it simply reflects the latest release deployed to production. However, if you prefer to use it, you should merge each release branch and each production tag back to the production branch.
Example Workflow
Suppose we need to release a new version:
- Merge all desired features into the
dev
branch. - Create a
release-v10
branch. - Tag it as
v10.0
and send it for QA. - Continue work on the
dev
branch for future features. - QA finds bugs.
- Fix the bugs directly in
release-v10
, then tag it asv10.1
. - QA finds bugs impacting both the current and previous release (
v9.5
). - Fix the bugs in
release-v9
, tag it asv9.6
. - QA validates
v9.6
and deploys it to production. release-v10
merges changes fromrelease-v9
.- Tag
v10.2
and deploy it to production after validation.
Related Topics
Cascading Changes
Whenever changes are made to a release branch, cascade these changes to dev
and all higher release branches. For example, any change made in release-v9
should be propagated to release-v10
and dev
.
Deleting Branches
Old release branches should be deleted once a newer version has been deployed to production. Recreating a release branch from a tag is straightforward and instantaneous.
Old feature branches can be deleted at your discretion.
Version File Updates
Version files should be updated whenever a new branch is created.
Stay in Sync
Use a git management tool that ensures your pull requests (PRs) are synchronized with the dev
branch before merging.
Build with Docker
Use Docker to manage builds. This approach makes it easy to change environments (e.g., JVM version, Linux distribution) within isolated branches.
Isolated Test Environment
Everything should be runnable in an isolated environment. Shared databases are challenging to maintain during concurrent tests.
Release Note Generation
With each new tag, generate two release notes:
- Issues resolved since the last tag for QA to prioritize testing.
- Issues addressed since the last production release for end-users to know what’s new.
GIT makes this relatively simple.
Deployment
Deployment should be seamless and automated.
Alternatives
Gitflow
Gitflow involves a lot of process overhead with minimal benefits, aside from having very clean version branches for releases or hotfixes.