Skip to main content

Version Grammar

This guide explains how version strings work in plain English. Whether you're tagging releases, configuring CI/CD, or just trying to understand what v1.2.3-beta.1+build.456 means, this page has you covered.

For the formal specification, see the EBNF grammar file.

What is a Version String?

A version string is a structured way to communicate information about a software release. Instead of saying "the new version" or "the February update," version strings provide precise, comparable identifiers.

v1.2.3-beta.1+build.456

This single string tells us:

  • It's version 1.2.3
  • It's a beta pre-release (the first one)
  • It was built with identifier "build.456"

Anatomy of a Version

Every version string follows this pattern:

[prefix]MAJOR.MINOR.PATCH[-prerelease][+metadata]

Let's break down each part:

The Prefix (Optional)

v1.0.0
V2.3.4
1.0.0

The prefix is a single letter that appears before the numbers. Versionator only allows:

PrefixMeaning
vLowercase "v" (most common)
VUppercase "V"
(none)No prefix at all

Why only v/V? This follows conventions established by Go modules, npm, and git tags. Other prefixes like "ver" or "version-" are not supported because they create inconsistency across tools.

The Core Version (Required)

The core version has three numbers separated by dots:

MAJOR.MINOR.PATCH
ComponentWhat It MeansWhen to Change
MAJORBreaking changesYou changed something that breaks existing code
MINORNew featuresYou added something new (backwards compatible)
PATCHBug fixesYou fixed a bug (backwards compatible)

Examples:

VersionInterpretation
1.0.0First stable release
2.0.0Major rewrite or breaking changes from 1.x
1.5.0Added new features to version 1
1.5.3Third bug fix release for version 1.5

The rules:

  • All three numbers are required
  • Numbers can be any non-negative integer (0, 1, 2, ... 999, etc.)
  • No leading zeros (use 1.2.3, not 01.02.03)

Pre-release Identifier (Optional)

Pre-release versions come before the final release. They're for testing and early access.

1.0.0-alpha
1.0.0-beta.1
1.0.0-rc.2

The pre-release starts with a hyphen (-) followed by identifiers separated by dots.

Common pre-release labels:

LabelMeaningTypical Use
alphaVery early, unstableInternal testing
betaFeature complete, may have bugsExternal testing
rcRelease candidateFinal testing before release

Adding numbers:

You can add numbers to track iterations:

1.0.0-alpha      # First alpha
1.0.0-alpha.1 # Alpha iteration 1
1.0.0-alpha.2 # Alpha iteration 2
1.0.0-beta # Move to beta
1.0.0-beta.1 # Beta iteration 1
1.0.0-rc.1 # First release candidate
1.0.0-rc.2 # Second release candidate
1.0.0 # Final release!

Important: A pre-release version is always less than the normal version:

1.0.0-alpha < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0

This means 1.0.0-rc.1 comes before 1.0.0 in sort order, which is what you want.

Build Metadata (Optional)

Build metadata provides additional information about a specific build. It starts with a plus sign (+).

1.0.0+20241215
1.0.0+build.123
1.0.0-beta.1+sha.abc1234

Common metadata:

ExampleWhat It Contains
+20241215Build date
+build.123CI build number
+sha.abc1234Git commit hash
+20241215.abc1234Date and commit

Important: Build metadata is ignored when comparing versions:

1.0.0+build.1 == 1.0.0+build.999  # Same version!

This makes sense because both builds represent the same release, just built at different times or on different machines.

Putting It All Together

Here are complete examples showing all parts:

VersionPrefixCorePre-releaseMetadata
1.0.0-1.0.0--
v1.0.0v1.0.0--
v2.1.3-alphav2.1.3alpha-
v1.0.0-rc.1v1.0.0rc.1-
1.0.0+build.456-1.0.0-build.456
v3.2.1-beta.2+20241215.abc1234v3.2.1beta.220241215.abc1234

Character Rules

Version strings have specific rules about what characters are allowed:

In Core Version Numbers

Only digits 0-9. No letters, no symbols.

1.2.3      ✓ Valid
1.2.3a ✗ Invalid (letter in patch)
1.2.3.4 ✗ Invalid for SemVer (too many parts)

In Pre-release and Metadata

Allowed characters:

  • Digits: 0-9
  • Letters: a-z, A-Z
  • Hyphen: -
1.0.0-alpha       ✓ Valid
1.0.0-alpha.1 ✓ Valid
1.0.0-my-feature ✓ Valid (hyphens OK)
1.0.0-alpha_1 ✗ Invalid (underscore not allowed)
1.0.0-alpha 1 ✗ Invalid (space not allowed)

No Leading Zeros in Numbers

Numeric identifiers cannot have leading zeros:

1.0.0-alpha.1    ✓ Valid
1.0.0-alpha.01 ✗ Invalid (leading zero)
1.0.0-alpha.0 ✓ Valid (just zero is fine)

Version Comparison (Sorting)

When versions are sorted, they follow these rules:

1. Compare Core Numbers Left to Right

1.0.0 < 2.0.0      # Major differs
1.1.0 < 1.2.0 # Minor differs
1.1.1 < 1.1.2 # Patch differs

2. Pre-release Comes Before Release

1.0.0-alpha < 1.0.0
1.0.0-rc.99 < 1.0.0

3. Pre-release Identifiers Compare Left to Right

1.0.0-alpha < 1.0.0-beta           # "alpha" < "beta" alphabetically
1.0.0-alpha.1 < 1.0.0-alpha.2 # 1 < 2 numerically
1.0.0-alpha < 1.0.0-alpha.1 # Shorter < longer

4. Numbers Compare as Numbers, Not Text

1.0.0-alpha.2 < 1.0.0-alpha.10     # 2 < 10 (numeric comparison)

This is important! Text sorting would put 10 before 2, but version sorting correctly recognizes 10 > 2.

5. Build Metadata is Ignored

1.0.0+build.1 == 1.0.0+build.999   # Same precedence

Complete Example: A Release Cycle

Here's how versions might progress through a typical release:

0.1.0              # Initial development
0.2.0 # More features
0.9.0 # Getting close to 1.0
1.0.0-alpha # First 1.0 preview
1.0.0-alpha.1 # Alpha bugfix
1.0.0-alpha.2 # More alpha fixes
1.0.0-beta # Feature complete, testing
1.0.0-beta.1 # Beta bugfix
1.0.0-rc.1 # Release candidate
1.0.0-rc.2 # Fix last-minute issue
1.0.0 # Official release!
1.0.1 # Patch release (bugfix)
1.1.0 # Minor release (new feature)
2.0.0 # Major release (breaking changes)

Special Formats

Go Module Versions

Go requires a v prefix and has a special format for untagged commits called "pseudo-versions":

v1.2.3                                    # Normal version
v0.0.0-20241215103045-abc123def456 # Pseudo-version

The pseudo-version contains:

  • A base version (v0.0.0)
  • A timestamp (20241215103045 = 2024-12-15 10:30:45 UTC)
  • A commit hash prefix (abc123def456)

Dynamic Content in Versionator

Versionator can generate version components dynamically using templates:

# .versionator.yaml
prerelease:
template: "alpha-{{CommitsSinceTag}}"
metadata:
template: "{{BuildDateTimeCompact}}.{{ShortHash}}"

This might produce: 1.0.0-alpha-5+20241215103045.abc1234

The {{...}} parts are replaced with actual values when you run versionator.

Quick Reference

PartRequiredStarts WithAllowed CharactersExample
PrefixNo-v or V onlyv
MajorYes-digits1
MinorYes.digits.2
PatchYes.digits.3
Pre-releaseNo-digits, letters, -, .-beta.1
MetadataNo+digits, letters, -, .+build.456

See Also