What Semantic Versioning Actually Means

Semantic versioning (SemVer) is a convention for numbering software releases. The version string has three parts: MAJOR.MINOR.PATCH. Each part represents a different kind of change, and the rules for incrementing them are specific. This is not a suggestion — it is a contract between the software author and its users.

The convention states that MAJOR increments for breaking changes, MINOR for new functionality that does not break existing code, and PATCH for bug fixes. A version 1.0.0 means the public API is considered stable. Before that, versions 0.x.y indicate ongoing development where breaking changes are still acceptable.

The key word is breaking. A breaking change is any change that causes existing consumers of the software to stop working, or to behave differently in a way that matters. This is not about internal refactoring or performance improvements. It is about the interface — the API, the CLI, the file format, the configuration schema — that other code depends on.

What Counts as a Breaking Change

A breaking change occurs when the contract between the software and its consumers is altered in a way that existing consumers cannot handle. The most common examples are:

Adding a new function or method is not a breaking change. Adding a new field to a response is not a breaking change, provided consumers that do not understand the new field can safely ignore it. Removing an optional field is not a breaking change, provided consumers that do not supply it can still function.

The most common mistake is treating any change as breaking. If a change is backward-compatible — if existing consumers continue to work without modification — it should not trigger a MAJOR increment. This is where the convention gets practical value: it lets consumers know when to test and when they can update with confidence.

What Does Not Count as Breaking

Not every change that affects behavior is breaking. The distinction matters because it determines whether consumers can update automatically or must review the release notes carefully.

Adding a new feature is not breaking. Adding a new parameter to a function, provided it is optional, is not breaking. Changing an implementation detail (e.g., switching from a hash map to a tree) is not breaking, as long as the observable behavior is the same. Fixing a bug is not breaking, even if the fix changes behavior, provided the new behavior is more correct than the old one and existing consumers can tolerate it.

Performance improvements are not breaking. Documentation changes are not breaking. Internal refactoring is not breaking. These are changes that affect the software but not the contract with consumers.

How to Decide in Practice

The most reliable approach is to ask a simple question: will existing consumers of this software break if they upgrade to this version?

If the answer is yes, increment MAJOR. If the answer is no, but new functionality is being added, increment MINOR. If only bugs are being fixed, increment PATCH. This is the core of the convention, and it is what most teams get right.

When in doubt, consider the scope of the change. A change that affects a single function used by one consumer is less likely to be breaking than a change to a core API used by many. But the convention does not require you to measure scope — it requires you to measure compatibility. If the contract is preserved, the version does not need to change.

Teams that struggle with this convention often do so because they conflate "change" with "breaking change." A release with fifty changes is not necessarily a MAJOR release if most of those changes are backward-compatible. Conversely, a release with a single breaking change is a MAJOR release, regardless of how many other changes it contains.

Common Mistakes and How to Avoid Them

The most frequent error is incrementing MAJOR when a MINOR would suffice. This often happens when teams treat any visible change as breaking, even when existing consumers would not notice. The result is version numbers that grow faster than the actual rate of breaking changes, which makes the convention less useful.

Another common mistake is not incrementing MAJOR when a breaking change occurs. This is more serious because it breaks the contract. Consumers who update to a MINOR release expecting backward compatibility will find that their code no longer works. This is why the convention is important: it is a signal that consumers can rely on.

A third mistake is treating PATCH releases as purely for bug fixes. While this is the conventional use, it is not a strict rule. A PATCH release can include new functionality, provided that functionality is backward-compatible and does not change the behavior of existing code.

Teams that want to be more precise about their versioning can use tools that help them track changes and generate release notes. ChangelogPro transforms raw commit messages and bullet points into release notes, which helps teams communicate what has changed and whether it is breaking. This is useful for teams that want to make the convention more visible to their consumers.

Version Numbers Beyond Three Parts

SemVer allows for additional parts of the version string, such as pre-release identifiers (1.0.0-alpha) and build metadata (1.0.0+build.123). These are not part of the core convention and do not affect the MAJOR.MINOR.PATCH semantics. Pre-release versions are used during development and testing. Build metadata is used to identify the exact build of a release.

When a pre-release version is published, it is typically followed by a release candidate and then the final release. The final release has the same MAJOR.MINOR.PATCH number as the last pre-release, but without the pre-release identifier. This is the standard practice, and it is what most consumers expect.

Conclusion

Semantic versioning is a simple convention with practical value. It tells consumers what to expect from a release and helps teams communicate about their software. The key is to understand what counts as a breaking change and to be consistent about it. A MAJOR increment means a breaking change. A MINOR increment means new functionality. A PATCH increment means a bug fix. This is the core of the convention, and it is what most teams should follow.

Tool mentioned: ChangelogPro