Semantic Versioning and Automated Changelogs
How to combine SemVer with automated changelog generation using Git tags, conventional commits, and PR labels for versioned release notes.
Semantic versioning (SemVer) and automated changelogs are two practices that work well independently but work even better together. SemVer gives your releases a structured version number that communicates the nature of changes. Automated changelogs give each version a human-readable description of what actually changed. Combined, they create a release communication system that requires minimal manual effort while giving consumers maximum clarity.
If you are already using SemVer, adding automated changelogs is straightforward. If you are not using SemVer yet, this guide will help you adopt both at once.
A quick SemVer refresher
Semantic versioning uses a three-part version number: MAJOR.MINOR.PATCH. The rules are simple: increment MAJOR when you make breaking changes, MINOR when you add functionality in a backwards-compatible way, and PATCH when you make backwards-compatible bug fixes.
The version number is a contract with your consumers. When they see a patch bump (2.1.0 to 2.1.1), they know they can upgrade without worry. When they see a major bump (2.x to 3.0.0), they know there are breaking changes that might require code changes on their end.
In practice, the hardest part of SemVer is deciding what counts as a "breaking change." Removing a public API endpoint is obviously breaking. But what about changing the default value of an optional parameter? Or adding a required field to a response object? Teams need to agree on these boundaries upfront, or the version numbers become meaningless.
Connecting versions to changelogs
Every version should have a corresponding changelog entry that explains what changed. The format that has become the de facto standard looks something like this:
Group changes by type (Added, Changed, Fixed, Removed) under the version header. Each entry describes a single change in one sentence. Breaking changes get called out explicitly, ideally with migration instructions.
Manually maintaining this format is tedious but doable at low release frequencies. At high frequencies, it breaks down for the same reasons all manual changelogs break down: people forget, quality varies, and the person who made the change is not always the person who writes the entry.
Using Git tags as version boundaries
The key to automating versioned changelogs is Git tags. When you tag a commit with v2.1.0, you create a boundary marker in your Git history. Everything between v2.0.0 and v2.1.0 belongs to that version. An automated tool can collect all PRs merged between those two tags and generate the changelog for that version.
This approach has a nice property: the version number and the changelog are both derived from the same source of truth (Git history). There is no gap between what the version says and what the changelog describes because they are both generated from the same set of commits.
The workflow looks like this: merge PRs as normal, each generating individual release notes. When you are ready to cut a release, create a Git tag. The automation collects all changes since the last tag, groups them, and produces a versioned changelog entry.
Conventional commits and PR labels
For the automation to categorize changes (Added vs. Fixed vs. Breaking), it needs some signal about the nature of each change. There are two common approaches:
Conventional commits embed the category in the commit message: feat: add export to CSV, fix: handle null dates in report, breaking: remove legacy auth endpoint. The prefix tells the automation what type of change this is, which determines both the changelog category and (in some setups) the version bump.
PR labels achieve the same result through GitHub labels like feature, bug, breaking-change. The automation reads the labels from each PR and categorizes the changelog entry accordingly. This approach is less intrusive than conventional commits because it does not change the commit message format.
Either approach works. Conventional commits are more popular in open source projects. PR labels tend to work better for product teams because they are visual, easy to apply, and do not require changing how developers write commit messages.
Automated version bumping
Once you have categorized changes, you can automate the version bump itself. If all changes since the last tag are bug fixes, bump the patch version. If there are new features, bump the minor version. If there are breaking changes, bump the major version.
This is powerful but requires discipline. If someone labels a breaking change as a feature, the automation will produce a minor bump instead of a major bump, which violates the SemVer contract. Most teams handle this with a simple review step: before tagging a release, someone checks the generated changelog and confirms the version bump is correct.
For teams that are not ready for fully automated version bumping, a semi-automated approach works well: the tool generates the changelog and suggests a version number, but a human makes the final call on the tag.
Putting it together
Here is the full workflow for a team using SemVer with automated changelogs:
- Developers open PRs with labels or conventional commit prefixes indicating the change type
- On merge, an automated release note is generated for the individual PR
- When ready to release, a Git tag is created (manually or via automation)
- The automation collects all PR notes since the last tag and produces a versioned changelog
- The changelog is categorized (Added, Fixed, Changed, etc.) based on labels or commit prefixes
- The version number is validated against the change categories (breaking = major, feature = minor, fix = patch)
- The versioned changelog is published
Each step can be manual, semi-automated, or fully automated depending on your team's comfort level. The important thing is that the flow from code change to versioned, categorized changelog is continuous and consistent.
If you want to set this up for your team, check out the Engammo documentation for guides on tag-based release note generation and the API reference for programmatic access.