The Nightmare of “Manual Versioning”
Have you ever spent hours reviewing Git logs just to write a CHANGELOG file? I used to be stuck in this exhausting loop: checking whether a change was a feature or a fix, updating the version number in package.json, and then manually typing out every single change. Just a moment of distraction, and I’d forget to update the version or miss a critical commit.
Once, I forgot to update the version but pushed the code to production anyway. The monitoring system flagged errors for the old version, leaving my colleagues confused about whether the new features were actually live. After that mistake, I decided to automate everything. Semantic-release has been the solution, saving me at least 15-20 minutes every single time I release.
How Does Semantic-release Work?
This tool relies on Conventional Commits to automatically determine the next version number. You no longer need to manually decide between Major, Minor, or Patch updates.
The rules are extremely simple. If you use fix: ..., it increments the Patch version. If you use feat: ..., it increments the Minor version. More importantly, when the BREAKING CHANGE keyword is present, the system automatically bumps it to a Major version. Everything follows precise computer logic.
Installing Necessary Packages
To get started, install the following plugins as devDependencies. Here is the toolset I typically use for Node.js environments:
npm install --save-dev semantic-release @semantic-release/changelog @semantic-release/git @semantic-release/github @semantic-release/npm
Each plugin handles a specific task:
- commit-analyzer: Analyzes commit history to determine the new version.
- release-notes-generator: Automatically compiles content for the CHANGELOG.
- changelog: Writes changes to the
CHANGELOG.mdfile. - git: Automatically commits updated files back to the repository.
- github: Creates a GitHub Release with tags and version notes.
“Real-world” .releaserc Configuration
I usually use a .releaserc file for centralized configuration. Here is the template I apply to my real-world projects:
{
"branches": ["main", "master"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
"@semantic-release/npm",
[
"@semantic-release/git",
{
"assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
"@semantic-release/github"
]
}
Important Note: Including [skip ci] in the commit message prevents infinite CI/CD loops. Without this, the bot’s commit would trigger the pipeline repeatedly, wasting resources and potentially freezing the system.
Integrating with GitHub Actions
The true power of Semantic-release lies in the automated pipeline. Create a .github/workflows/release.yml file with the following content:
name: Release
on:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 'lts/*'
- run: npm ci
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
The GITHUB_TOKEN variable is provided by default. However, you need to add NPM_TOKEN to your Secrets if you want to publish the package to npm. For internal projects, set "npmPublish": false in the plugin configuration.
Testing Before Deployment
Before merging your code, you should run a test in dry-run mode to check the logs:
npx semantic-release --dry-run
This command shows you exactly what the next version will be without modifying files or creating real tags. Once everything looks good, just git push and let the pipeline handle the rest.
The result is a professional Releases page on GitHub. Your colleagues can grasp every change at a glance without having to ask you a single question.
Practical Lessons Learned
First, break the habit of using vague commit messages like fix: update code or feat: done. These meaningless descriptions will appear in the CHANGELOG and can reduce the project’s credibility in the eyes of the client.
Second, prioritize using Squash and Merge when working in teams. This allows you to polish the commit message one last time before it enters the main branch history.
Finally, install commitlint and husky. This duo will block non-standard commits right at the developer’s machine, ensuring the process always runs smoothly.
Adopting Semantic-release not only frees up your time but also builds a professional workflow. Machines are better at repetitive tasks than humans, so why not take advantage of that?

