Skip to content

semantic versioning (SemVer)

Semantic versioning, or SemVer, is a convention for numbering software releases as MAJOR.MINOR.PATCH, where each of the three numbers signals how risky upgrading to that release is for the projects that depend on it. The current specification, SemVer 2.0.0, ties each number to a category of change in a project’s public API:

  • MAJOR: Increases on incompatible changes that can break existing callers.
  • MINOR: Increases when a release adds functionality in a backward-compatible way.
  • PATCH: Increases on backward-compatible bug fixes.

An upgrade from 2.4.1 to 3.0.0 warns a Python project that something it relies on may have changed, while 2.4.1 to 2.4.2 promises a safe drop-in fix, the kind a hotfix ships. The number that moves depends on the kind of change in the release:

Flowchart with a teal Change box fanning down to three white boxes (Breaking change, New feature, Bug fix), each leading to Bump MAJOR, MINOR, or PATCH, merging into a yellow MAJOR.MINOR.PATCH node.
The riskiest change since the last release decides which number you bump.

How It Shows Up in Practice

A Python developer meets SemVer most often when pinning dependencies. A specifier such as requests >= 2.31, < 3.0 in a pyproject.toml file bets that the package’s maintainers follow SemVer, so any later 2.x release is safe to accept but a 3.0 is not. Maintainers signal the same contract from the other side by tagging releases in version control with a name such as v1.4.2 and publishing matching versions to the Python Package Index (PyPI).

SemVer also reserves room for releases that aren’t finished. A pre-release appends a hyphen, as in 2.0.0-alpha.1 or 2.0.0-rc.1, and always sorts below the matching final version. That 2.0.0-rc.1 is the release candidate a team promotes through a staging environment before cutting 2.0.0.

Build metadata can follow a plus sign, as in 2.0.0+20260529, and is ignored when versions are compared. One rule catches newcomers: any 0.y.z version marks initial development, where the spec says anything may change at any time, so a library still on 0.x isn’t promising a stable API yet.

The standard library is enough to derive the next version for each kind of change:

Language: Python
def bump(version, change):
    major, minor, patch = (int(part) for part in version.split("."))
    if change == "breaking":
        return f"{major + 1}.0.0"
    if change == "feature":
        return f"{major}.{minor + 1}.0"
    return f"{major}.{minor}.{patch + 1}"


current = "2.4.1"
for change in ("bug fix", "feature", "breaking"):
    print(f"{change:>8}: {bump(current, change)}")
Language: Program Output
 bug fix: 2.4.2
 feature: 2.5.0
breaking: 3.0.0

SemVer in Python Packaging

Python packaging doesn’t mandate SemVer, and its own version rules, defined in PEP 440, differ in the details. PEP 440 calls the three numbers major.minor.micro and treats the MAJOR.MINOR.PATCH core of SemVer as fully compatible and encouraged. The spellings diverge, though. A Python pre-release is written 2.0.0rc1, not SemVer’s 2.0.0-rc.1, and PEP 440 rejects the hyphen and plus-sign forms in a package’s public version.

The two schemes meet at the compatible-release operator, ~=. The specifier requests ~= 2.31.0 expands to >= 2.31.0, == 2.31.*, which trusts the dependency to follow SemVer: accept any new patch, refuse a new minor.

A library that adopts SemVer, or PEP 440’s compatible subset, and documents the policy in a changelog lets downstream projects rely on ~= for dependency management. To see how a single mis-numbered release ripples outward through a dependency graph, read transitive dependency.

Tutorial

How to Publish an Open-Source Python Package to PyPI

In this step-by-step tutorial, you’ll learn how to create a Python package for your project and how to publish it to PyPI, the Python Package Repository. Quickly get up to speed on everything from naming your package to configuring it using setup.cfg.

intermediate best-practices tools

For additional information on related topics, take a look at the following resources:


By Martin Breuss • Updated May 29, 2026