
September 10, 2026
10 min read
By Kokil Thapa | Last reviewed: September 2026
Your git log is full of messages like "fix stuff" and "updates." That noise makes every release a guessing game. Conventional Commits and Semantic Versioning fix that by tying each commit to a machine-readable type and mapping those types to MAJOR, MINOR, and PATCH bumps. On production Laravel apps I maintain with Git hooks and CI pipelines, this pairing cuts release friction and keeps changelogs honest. This guide shows the format, the bump rules, and how to wire it into real PHP workflows in 2026.
What are Conventional Commits and Semantic Versioning?
They solve two related problems. Conventional Commits standardise how you write commit messages. Semantic Versioning (SemVer) standardises how version numbers change when behaviour changes.
A conventional commit looks like this:
feat(checkout): add Khalti payment callback handler
BREAKING CHANGE: payment status endpoint now returns JSON instead of plain text
SemVer expresses compatibility as MAJOR.MINOR.PATCH — for example 2.4.1. A breaking change bumps MAJOR. A backward-compatible feature bumps MINOR. A backward-compatible fix bumps PATCH.
The official specs live at conventionalcommits.org and semver.org. Treat them as the source of truth when your team debates edge cases.
You do not need fancy tooling on day one. A team that agrees on the format and reviews PR titles consistently already gains most of the benefit. Automation comes next.
How do you write a valid Conventional Commit message?
The format is strict but small. Every commit has a type, an optional scope, a short description, an optional body, and optional footers.
Header structure
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Common types and their typical SemVer effect:
- feat — new user-facing behaviour → MINOR bump
- fix — bug fix, no API break → PATCH bump
- docs, style, test, chore, ci, build, refactor — usually no version bump unless they change runtime behaviour
- perf — performance fix; many teams treat it as PATCH
- BREAKING CHANGE footer or
!after type → MAJOR bump
Examples that pass review
fix(auth): reject expired Sanctum tokens on API routes
feat(cart): support NPR and USD display on checkout page
refactor(orders)!: rename OrderService methods for clarity
BREAKING CHANGE: getTotal() is now calculateTotal()
Keep the subject line under 72 characters. Use imperative mood: "add handler," not "added handler." Match what you already expect from good git hygiene practices.
Scopes that help Laravel teams
Scopes should mirror modules your team owns: auth, billing, api, queue, deploy. On a legal-tech portal, scopes like documents or booking make changelogs readable for non-developers.
How does Semantic Versioning decide MAJOR, MINOR, or PATCH?
SemVer is a contract with downstream consumers. Version 1.4.2 means: same MAJOR → compatible; higher MINOR → new features, still compatible; higher PATCH → fixes only.
Map conventional types to bumps like this:
- MAJOR — any breaking change to public API, database schema consumers rely on, or config keys external tools read
- MINOR — new backward-compatible functionality
- PATCH — backward-compatible bug fixes
Pre-release labels (1.0.0-beta.1) and build metadata (1.0.0+20260910) are valid SemVer. Most PHP apps ship simple three-part numbers unless you publish Composer packages.
| Commit signal | SemVer bump | Example | Typical consumer impact |
|---|---|---|---|
BREAKING CHANGE or type! | MAJOR | 1.4.2 → 2.0.0 | Must update integration code |
feat: | MINOR | 1.4.2 → 1.5.0 | New optional endpoints or fields |
fix: / perf: | PATCH | 1.4.2 → 1.4.3 | Same API, corrected behaviour |
docs:, chore:, ci: | None (usually) | 1.4.2 → 1.4.2 | No runtime change |
Reset PATCH and MINOR to zero when you bump a higher segment. After 1.4.9, a feature release becomes 1.5.0, not 1.5.9.
How do you automate Conventional Commits and Semantic Versioning in CI?
Manual versioning fails when releases get rushed. Automation enforces the contract. A typical stack for PHP/Laravel projects includes commitlint, a release tool, and CI gates.
Enforce messages locally and in CI
Install commitlint with the conventional config:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
echo "export default { extends: ['@commitlint/config-conventional'] };" \
> commitlint.config.js
Add a Husky hook or call commitlint from your existing pre-commit workflow. In GitLab CI, fail the pipeline when messages on a merge request do not parse:
validate-commits:
stage: test
script:
- npm ci
- npx commitlint --from ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --to ${CI_COMMIT_SHA}
This catches bad messages before they hit main.
Generate versions and changelogs
Semantic-release reads commits since the last tag and publishes a new version. For PHP Composer packages, pair it with a composer.json version field update. For deploy-only apps, tagging alone may be enough.
Alternative: use standard-version or release-please if you want explicit control over release PRs. I have used semantic-release on sister sites that share a Deployer 7 pipeline — the tag triggers deploy, and rollback stays a symlink swap.
Wire releases to Laravel and PHP projects
For a Laravel 13 app on PHP 8.3+, store the app version in config rather than hard-coding Blade footers:
// config/app.php
'version' => env('APP_VERSION', '1.0.0'),
Inject at deploy time from the git tag:
export APP_VERSION=$(git describe --tags --always)
php artisan config:cache
Composer 2.10 projects that publish libraries should bump composer.json version in the same commit as the tag. Consumers pin with caret ranges: "vendor/package": "^2.3".
Validate JSON changelogs or release manifests with a JSON formatter before publishing API metadata. For broader pipeline hardening, see AI code review in CI and testing and optimization practices.
What mistakes break Conventional Commits and Semantic Versioning in production?
Teams adopt the format and still ship confusing versions. These failures show up repeatedly on client projects and internal packages.
Squash merges that hide breaking changes
GitHub and GitLab squash merge by default. If the squash title ignores conventional format, your release tool sees one vague message. Fix: enforce PR title format and pass the PR title into the squash commit.
Mixing app versioning with API versioning
A Laravel app at 3.1.0 can expose /api/v1 and /api/v2 simultaneously. App SemVer and URL API version are related but not identical. Document both in your API versioning strategy.
Shipping breaking changes as PATCH
Renaming a public Eloquent resource field breaks mobile clients. That is MAJOR for the API contract even if the commit author wrote fix:. Code review must catch semantic intent, not just syntax.
Never releasing 1.0.0
SemVer before 1.0.0 treats MINOR as potentially breaking. For production business apps — booking portals, eCommerce stores, legal workflows — pick 1.0.0 at go-live and bump from there.
On projects like Adventure Third Pole Trek, clear release notes help operations staff track booking-module changes without reading git. The same discipline applies to client portals with document uploads where compliance questions appear months later.
Monorepo complexity
One repository with multiple deployable packages needs scoped commits and per-package version tags. Tools like release-please handle multi-artifact repos better than a single global tag.
For infrastructure repos, align with patterns from Ansible provisioning playbooks and Linux administration workflows so server and app versions stay traceable.
How should small teams in Nepal adopt this without over-engineering?
Not every SMB site needs semantic-release on day one. A phased rollout keeps cost sensible — often Rs 0 in tooling, mostly discipline.
- Week 1: Document allowed types in CONTRIBUTING.md. Require conventional PR titles in code review.
- Week 2: Add commitlint to CI on the main branch only.
- Week 3: Tag releases manually using SemVer rules until the log is clean.
- Week 4: Automate changelog generation and tie tags to Deployer or GitLab deploy jobs.
If you publish a reusable Composer package, invest earlier. If you run a single WooCommerce or WordPress 7.1 site with no external API, PATCH tags on plugin updates may be enough.
Founders comparing build-vs-buy for custom platforms should weigh release discipline as part of custom software development and ongoing support and maintenance. Good versioning reduces emergency calls after deploy.
Enterprise teams with multiple integrations benefit from pairing SemVer with explicit API development standards and enterprise application governance.
Developers maintaining long-lived codebases can cross-check history using git reflog recovery when a bad release tag needs untangling.
Key Takeaways
- Conventional Commits use
type(scope): descriptionplus optionalBREAKING CHANGEfooters so machines and humans parse intent. - Semantic Versioning maps those signals to MAJOR (break), MINOR (feature), and PATCH (fix) with zero-reset rules between segments.
- Enforce format in CI with commitlint before automating tags, changelogs, or deploy triggers.
- Squash-merge PR titles must stay conventional or your release history collapses into noise.
- Separate app version from API URL version — both need documentation for integrators.
- Start with team agreement and manual tags; add semantic-release once commits are consistently clean.
People Also Ask
Do Conventional Commits require Semantic Versioning?
No. Conventional Commits stand alone as a message convention. SemVer is the versioning scheme most teams pair with them because the commit types map cleanly to bump rules. You can use conventional messages without automated versioning.
What is the difference between feat and fix in Conventional Commits?
feat introduces new capability a user or API consumer can access. fix corrects incorrect behaviour without adding features. Both are usually production-impacting; docs and chore commits typically skip version bumps.
When should you ship a MAJOR version bump?
Ship MAJOR when you break backward compatibility: removed endpoints, renamed response fields, changed authentication requirements, or migrations that force coordinated client updates. Mark it with BREAKING CHANGE: or ! after the type.
Can you use Conventional Commits with Laravel and PHP projects?
Yes. Laravel apps, Composer packages, and WordPress plugins all benefit. Store the running version in config or composer.json, inject it at deploy from git tags, and run PHPUnit plus static analysis in CI before any automated tag promotes to production.
Ship predictable releases with Conventional Commits and Semantic Versioning
Structured commits turn git history into a release contract. SemVer gives integrators a language for compatibility. Together they reduce Friday-night guesswork about what changed and whether it is safe to deploy. Start with PR title rules, add CI validation, then automate tags when the log earns it. If you want help wiring this into a Laravel pipeline, GitLab CI job, or Deployer workflow on your next build, contact us or explore web development services. For related reading, browse the blog, review portfolio releases, or read about how I work with production teams.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

