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:

  1. Merge all desired features into the dev branch.
  2. Create a release-v10 branch.
  3. Tag it as v10.0 and send it for QA.
  4. Continue work on the dev branch for future features.
  5. QA finds bugs.
  6. Fix the bugs directly in release-v10, then tag it as v10.1.
  7. QA finds bugs impacting both the current and previous release (v9.5).
  8. Fix the bugs in release-v9, tag it as v9.6.
  9. QA validates v9.6 and deploys it to production.
  10. release-v10 merges changes from release-v9.
  11. 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.