Why mark a release
Sooner or later someone asks, “Which exact version did we send to the customer?” or “Was this bug in last month’s release?” If the answer is “probably the commit from around the 14th”, you have a problem. A release marker removes the guesswork by giving a specific point in history a permanent, human-readable name.
Two practices work together here. Tags mark the exact state of the project at release. A changelog records, in plain language, what is different between releases. One is for the tool and for precision; the other is for people.
Git tags
A tag is a fixed label attached to one commit. Unlike a branch, it does not move when you make new commits. Git has two kinds:
- Lightweight tags are just a name pointing at a commit.
- Annotated tags also store the tagger, a date and a message, and are stored as full objects in the repository.
For releases, annotated tags are generally preferred because they carry the extra information. To create one on the current commit:
git tag -a v1.2.0 -m "Release 1.2.0"
To list tags:
git tag
To inspect what a tag points at:
git show v1.2.0
Tags are not sent to the shared repository by default. Push them explicitly:
git push origin v1.2.0
You can return to a tagged state to rebuild or investigate a released version. Check it out as you would any commit, and create a branch from it if you need to make a fix to that release.
Choosing version numbers
A version number should tell readers something. One widely used scheme is semantic versioning, which uses three numbers, MAJOR.MINOR.PATCH:
- Increase MAJOR when you make a change that breaks compatibility with earlier use.
- Increase MINOR when you add functionality in a compatible way.
- Increase PATCH for compatible fixes.
For example, going from 1.4.2 to 1.4.3 signals a fix, to 1.5.0 new compatible features, and to 2.0.0 a breaking change. This suits software libraries well. Hardware, firmware, documents and internal tools may use different schemes, such as dates or revision letters, and that is fine. What matters is that the scheme is explained, consistent, and applied the same way each time.
Prefix tags consistently, for instance with a v, so they sort and filter cleanly.
Tag the right commit
Before tagging, make sure the commit is the one you actually built and tested. A good routine is:
- Merge everything intended for the release.
- Run your tests and builds on that exact commit, ideally via continuous integration.
- Update the changelog and version number.
- Commit those, then tag that commit.
Avoid moving or deleting a published tag. Others may have built from it, and a tag that silently points somewhere new defeats its purpose. If you tagged the wrong commit and nobody has used it, you can remove it and retag, but once it is public the safer course is to make a new tag with a new version.
What a changelog is
A changelog is a file, often named CHANGELOG, kept at the top of the project. It lists notable changes for each release, newest first. It is not the same as the commit log. The commit log records every step in the developers’ working process, while the changelog summarises what matters to someone upgrading.
A reader of a changelog is usually asking:
- Do I need to change anything when I upgrade?
- Was the problem I reported fixed?
- What new things can I do?
A commit message like “Refactor parser loop” may be useful to a maintainer but irrelevant to a user. Writing the changelog means choosing and translating, in plain language.
A simple format
A common layout groups entries by release and by type of change:
## 1.2.0 - 2026-10-27
### Added
- Option to export results as CSV.
### Changed
- Default timeout is now 60 seconds.
### Fixed
- Crash when the input file was empty.
### Removed
- Support for the old configuration format.
The headings can be adapted. Many teams use Added, Changed, Fixed and Removed, sometimes with Deprecated and Security. Whatever you choose, keep entries short, put the date on each release, and call out anything that requires action from the reader, especially breaking changes.
Keeping it up to date
The easiest way to keep a changelog accurate is to add an entry as part of each change rather than reconstructing everything at release time. Some teams keep an “Unreleased” section at the top, add lines to it as work merges, and rename it to the version number when they tag the release.
Good commit messages and well-written pull requests make this much easier, since they preserve the reasons and wording you can reuse. Some teams generate drafts of release notes from commit messages or merged pull requests; that can save time, but the result usually needs editing, because machine-collected text tends to be written for developers rather than readers.
A stale changelog is worse than none, because it misleads. That is a general problem with documentation, and the same discipline applies as for any other record that must stay in step with the product.
Links between tags and changelog
Make the two agree. Each changelog heading should match a tag, so a reader can see the exact state of the project for any entry. Some teams add comparison links between versions; if your hosting service supports them, they are a convenient way to see everything that changed between two tags.
Requirements may differ
In some industries, release records must meet formal requirements about approvals, traceability and retention. Those requirements vary by country, sector and organisation, so follow the procedures that apply to you. For ordinary projects, a consistent tag and a readable changelog for every release is a small habit that pays off the first time someone asks what shipped.