Update Managing Releases authored by Slominski, Ryan's avatar Slominski, Ryan
---
title: Managing Releases
---
# Overview
There are many ways to manage software releases with Git. Git does not require any particular method to denote a release, though the most common method is a Git tag, and this is recommended. Further, Git Dev Platforms often provide a platform specific release feature as well. Git does not require any particular versioning scheme, but Semantic Versioning is a popular choice and recommended. The contents of release notes is also subjective, although at a minimum a Git diff between previous release tag and new release tag is recommended.
# Denoting Releases
Generally a Git tag should be used to mark a new release. There are alternatives however as it's possible that a Git Branch could be used instead of a tag to mark a release, though this is less common and only useful in certain workflows - especially since a branch can always be created from a tag at a later time. It's also possible to create a new separate repo on each release, though this is inefficient - many older software workflows that did not use version control systems such as Git relied on a new directory per release strategy so it's mentioned here for completeness.
Developer Platforms such as GitLab and GitHub provide a `releases` feature, that associate a Git tag with a release. This is recommended, as it allows easy association of release notes and release artifacts.
# Release Triggers
There are two common ways to trigger a new release workflow: On push of a VERSION file change, or on push of a Git tag change. We've landed on the VERSION file approach as explained below.
It's generally useful for software to be able to answer the question at runtime of what version it is. Further, many build tools such as Java Gradle and Python buildtools expect a version specifier in their configuration. Therefore, one major concern with releasing triggering is how to synchronize the version specifier used by the software and software build tools with the version specifier implied by a Git tag.
## VERSION file
In this approach a new release is created when a VERSION file is pushed to the main branch at the origin server (Dev Platform). This has the advantage of the tagged version of the software already containing a file with a matching version number. Running software can be configured to read this VERSION, and the build tools can as well. This has the advantage of being language agnostic as well as instead of figuring out how to insert the version into the build tools in a language specific way, the onus is reversed, and build tools can use their language specific mechanisms to read from a standard VERSION file.
## Tag Push
In this approach users push a new tag to trigger a release. However, since build tools need the version number readily available you must provide it somehow. Here are two common options:
1. Before pushing a tag, always update the VERSION file or language specific equivalent (such as Java build.gradle file or Python pyproject.toml) first. This is more work obviously, so not a great option.
2. Use a placeholder in the VERSION file or language specific equivalents in version control. Then on tag push the automated workflow dynamically inserts the real version into the placeholder on-demand. This could be accomplished via an environment variable. This is a viable approach and does have the advantage of avoiding the slight awkwardness of builds always report most recent version, even if you've added commits since and technically are a SNAPSHOT build.
**Note**: We found the VERSION file the simplest so went with that. We probably need to support either trigger though, as some build tools don't easily support reading from a VERSION file. For example, NodeJS apps use a static JSON file for configuration, which can't read the VERSION file. So you either must manually update the package.json before updating the VERSION file to create a release or else use an additional automation step that updates the package.json for you before auto-tagging.
# Version Schemes
There are many strategies for versioning, with simplest ones being a single integer representing revision.
A slightly more complicated one such as [Calendar Versioning](https://calver.org/) includes a date and often a single number representing the revision on that date. It turns out that many automated build tools attempt to resolve dependency compatibility automatically and this requires a standard agreed upon [Semantic Versioning](https://semver.org) scheme. In a semantic scheme API compatibility hints are provided, but assumes all libraries follow the same scheme consistently and correctly.
# The Default Branch
There are many strategies on how to treat the default branch, often called `main` (and formerly `master`), and on how / when / if to create additional branches. The simplest strategy is to only use the default branch. This is a good starting point. Often branches are useful in two specific scenarios:
1. Hot Fix on prior release
1. Feature branches for works in progress
The first case occurs when an issue arises in which changes to an already released code must be made, yet changes to an upcoming release are already committed onto the default branch. In this case, you can checkout the tag of the prior release in question, and create a branch from it so that the hot fix can be applied. Depending on your release automation, you may need to do a manual release for this hot fix.
The second case occurs when you have a large change that either needs to be saved periodically as incomplete as you work, requires review by someone else before merge, or needs to be isolated for clean documentation and tracking. Often all three are true at once. Since Git makes branching and merging so easy, it's often a reasonable strategy to just about always make changes in branches and then use the Pull Request (GitHub) or Merge Request (GitLab) approach to merge the changes. This provides a nice paper trail of changes, allows you to work on them incrementally at your leisure as you move between laptop and desktop (assuming you push the branches to origin), and signals a formal opportunity for review/approval.
Some workflows create a separate development branch in an attempt to minimize the possibility of the main branch having unstable code, but this may be overkill and may introduce more complexity than it's worth.
# Developer Platform
The developer platform in use, if any, such as GitHub or GitLab, can influence the approach used to release. GitLab for example emphasizes the Git Changelog trailer feature. GitHub on the other hand, provides a Marketplace of GitHub Actions that make it easy to summarize pull requests on release.
# Release Notes
At a minimum a Git diff, often called `compare` in GitLab / GitHub is useful to show what has changed. It's often useful to additionally provide any version migration notes for users, including any database scripts that need to be run to migrate the old software data to a new format, or discuss search and replace for API changes for example. These migration notes often need to be added manually. The diff and a list of merge requests / pull requests can often be provided automatically via GitHub/GitLab automation and repo APIs.
\ No newline at end of file