Zillowe FoundationZillowe Documentation

Specification

Zillowe Foundation Versioning Method Specification, Version 2.1

A clear and structured approach to software version management.

Introduction

In the world of software development, managing different versions across various stages of development, testing, and production can be complex. Without a clear system, it's easy to lose track of what a specific version represents, leading to confusion, deployment errors, and difficulties in tracking progress or issues. This is often compounded when dealing with multiple active development branches and varying levels of software maturity.

ZFVM (Zillowe Foundation Versioning Method) provides a comprehensive yet straightforward set of rules and guidelines for assigning and incrementing version identifiers. It builds upon the principles of Semantic Versioning (SemVer) by incorporating explicit information about the development Branch, the codebase's Status, and an optional Build identifier directly into the version string. This provides an immediate, clear understanding of a software version's context, stability, and intended use.

By adopting ZFVM, teams can:

  • Clearly communicate the state and purpose of any given software version.
  • Streamline coordination between development, QA, and operations.
  • Improve traceability and management of software releases throughout their lifecycle.
  • Provide multiple format options for different use cases and tools.

This document details the ZFVM V2.1 specification, providing a clear definition of its components, rules, and grammar.

About This Document

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

When this document refers to SemVer, it refers to Semantic Versioning 2.0.0. All SemVer requirements apply to the [X.Y.Z] component of a ZFVM version.

ZFVM is an independently maintained specification. It is not endorsed by, and does not represent, the Semantic Versioning project.

1. Conformance

The key words in this document define requirements for interoperability. A product is conforming if it satisfies the requirements below.

  • A conforming parser MUST accept the Canonical Full Form and the Contextual Form as defined in Section 9, and MUST reject any string that does not match the grammar in Section 5.
  • A conforming producer MUST emit the Canonical Full Form, or the Contextual Form where the branch is already unambiguous from context.
  • A conforming comparator MUST implement the precedence rules in Section 11.
  • A conforming mapper SHOULD implement the SemVer projection in Section 10.

An implementation MAY additionally accept or emit the Semantic, Symbolic, and Shorthand forms defined in Section 9. An implementation that accepts only a subset of forms SHOULD document precisely which forms it accepts.

Conformance to ZFVM does not require conformance to SemVer for the whole version string. The Canonical Full Form is not itself a valid SemVer string. See Section 10.

2. Notation and Character Set

  • A ZFVM version string MUST consist only of characters from the printable ASCII range, %x20-7E.
  • A ZFVM version string MUST NOT contain any control character, and MUST NOT contain the Unicode replacement character or zero-width characters.
  • A ZFVM version string SHOULD NOT exceed 255 characters. The [Build] component is the only component whose length is expected to vary meaningfully, and Section 4.4 bounds it separately.
  • Components are separated by exactly one space in space-separated forms, and by exactly one hyphen in hyphen-separated forms.
  • No leading or trailing space or hyphen is permitted on the overall string or on any individual component.
  • [Branch] and [Status] identifiers are case-sensitive and MUST match the identifiers given in Section 12 and Section 13 exactly. dev is not a valid [Branch]; Dev is.
  • Acronyms are likewise case-sensitive. pa is not valid; PA is.

3. Format

A ZFVM version string, in its full canonical form, MUST take the form:

[Branch] [Status] [X.Y.Z] [Build]
  • The [Build] component is OPTIONAL.
  • Exactly one space MUST separate each component.
  • The [Branch] component MUST use the short identifier from Section 12, not the full name. Dev Beta 1.2.3 is well-formed; Development Beta 1.2.3 is not. Full names are descriptive labels for humans and MUST NOT appear in a version string.
  • The [Status] component MUST use the identifier from Section 13 exactly as written, including internal hyphens. Pre-Alpha contains a hyphen and is written with that hyphen.

4. Components

4.1. Branch

  • The [Branch] component indicates the development environment or purpose.
  • It MUST be one of the four predefined identifiers listed in Section 12. The vocabulary is closed.
  • Each branch has a corresponding acronym for use in symbolic forms.
  • Example: Dev (acronym: D), Prod (acronym: P), Spec (acronym: S), Pub (acronym: Pb).

Note on Spec: The Spec branch denotes Special testing environments. It does not mean "specification". A ZFVM version such as Spec Alpha 1.0.0 identifies a build of a feature-specific test environment, not a version of this or any other specification.

4.2. Status

  • The [Status] component indicates the maturity level of the codebase.
  • It MUST be one of the five predefined identifiers listed in Section 13.
  • The identifiers define an ascending order of readiness: Pre-Alpha < Alpha < Beta < RC < Release.
  • Each status has a corresponding acronym for use in symbolic forms.
  • Example: Pre-Alpha (acronym: PA), Alpha (acronym: A), Beta (acronym: B), RC (acronym: RC), Release (acronym: R).

4.3. Semantic Version (X.Y.Z)

  • The [X.Y.Z] component MUST be a valid Semantic Version 2.0.0 core version.
  • X, Y, and Z MUST be non-negative integers and MUST NOT contain leading zeros. 1.2.3 is valid; 1.02.3 and 01.2.3 are not.
  • X is the Major version, Y is the Minor version, Z is the Patch version.

4.4. Build

  • The [Build] component is an OPTIONAL identifier that specifies a particular build instance of the software.
  • It is intended for tracking specific compilations, such as from a CI/CD pipeline or a source control commit.
  • A [Build] identifier MUST NOT be empty and SHOULD NOT exceed 32 characters.
  • A [Build] identifier MUST consist only of ASCII letters, digits, -, ., and _. This restriction is deliberate: it keeps the identifier valid for container image tags, file names, URL path segments, and package manager metadata without escaping.
  • Note that _ is valid in a ZFVM [Build] identifier but is not a legal SemVer identifier character. A projection under Section 10 MUST replace each _ with a - in the build metadata. See Section 10 for details.
  • Recognised [Build] formats include:
    • CI/CD run numbers: non-negative integers, for example 1234 or 9876
    • Commit hashes: short (7 to 8 characters) or full hashes, for example a7d3c1f or a7d3c1f8b2e4d9c0
    • Timestamps: ISO 8601 basic format or a compact variant, for example 20240521T1430Z or 20240521-1430
    • Custom identifiers: alphanumeric strings using the permitted character set, for example nightly-build or hotfix-123
  • Because the character set excludes the space, a [Build] identifier can never be confused with a following component.

5. Grammar

The canonical form is defined by the following ABNF, where SP is the ASCII space character (%x20), DIGIT is a decimal digit, ALPHA is an ASCII letter, and quoted strings are literal.

zfvm-full       = branch SP status SP semver [ SP build ]

branch          = "Prod" / "Dev" / "Spec" / "Pub"

status          = "Pre-Alpha" / "Alpha" / "Beta" / "RC" / "Release"

semver          = major "." minor "." patch
major           = "0" / ( %x31-39 *DIGIT )
minor           = "0" / ( %x31-39 *DIGIT )
patch           = "0" / ( %x31-39 *DIGIT )

build           = 1*build-char
build-char      = ALPHA / DIGIT / "-" / "." / "_"

The Contextual Form omits the [Branch] component:

zfvm-contextual = status SP semver [ SP build ]

The Symbolic Form (Section 9.4) is defined as:

zfvm-symbolic-a = b-acronym "-" s-acronym "-" semver
zfvm-symbolic-b = branch "-" status "-" semver
zfvm-symbolic-c = "b" 1*DIGIT "s" 1*DIGIT "-" semver

b-acronym       = "P" / "D" / "S" / "Pb"
s-acronym       = "PA" / "A" / "B" / "RC" / "R"

Grammar note: Symbolic Forms A and C are unambiguous by construction. Symbolic Form B is unambiguous only when the branch and status come from the closed vocabularies defined in Section 12 and Section 13. A parser MUST NOT apply a naive split on - to Form B, because Pre-Alpha contains an internal hyphen (Prod-Pre-Alpha-1.0.0 has three hyphen-separated tokens, not three fields). Symbolic Forms A and C are RECOMMENDED for automated processing for exactly this reason.

The Shorthand Form (Section 9.5) is defined as:

zfvm-shorthand  = "v" major [ "." minor ]

6. Incrementing X.Y.Z

ZFVM adopts the SemVer rules for the X.Y.Z portion unchanged.

  • Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
  • Version 1.0.0 and above define the public API. How the version number is incremented after this point depends on that API and how it changes.
  • Once a ZFVM version has been published or released, the contents of that version MUST NOT be modified. Any modification MUST be published as a new ZFVM version, which means a new X.Y.Z or a new [Build].
  • Patch version Z (as in x.y.Z where x > 0) MUST be incremented if only backward-compatible bug fixes are introduced. A bug fix is defined as an internal change that fixes incorrect behavior.
  • Minor version Y (as in x.Y.z where x > 0) MUST be incremented if new, backward-compatible functionality is introduced to the public API. Y MUST also be incremented if any public API functionality is marked as deprecated. Y MAY be incremented if substantial new functionality is introduced within the private code, and MAY include patch-level changes. Z MUST be reset to 0 when Y is incremented.
  • Major version X (as in X.y.z where X > 0) MUST be incremented if any backward-incompatible changes are introduced to the public API. It MAY also include minor and patch-level changes. Y and Z MUST be reset to 0 when X is incremented.
  • Major, minor, and patch versions MUST each increase numerically rather than lexically. 1.9.0 precedes 1.10.0, which precedes 1.11.0.

6.1. Interaction with Status and the zero major version

  • When X is 0, the [Status] MUST NOT be Release, and MUST NOT exceed RC. A 0.y.z codebase has, by SemVer definition, no stable public API, so asserting a Release status would contradict that guarantee. Prod RC 0.9.0 is well-formed; Prod Release 0.9.0 is not.
  • The public API referred to in this section is the API of the software being versioned, not the ZFVM specification itself.

7. Status Transitions

Section 6 governs X.Y.Z. This section governs what happens to X.Y.Z when the [Status] changes.

  • A change of [Status] alone MUST NOT increment X.Y.Z. Advancing maturity is not a change to the software.
  • X.Y.Z MUST remain unchanged across the entire status ladder, including the transition to Release. Dev Beta 1.2.3 is followed by Dev RC 1.2.3, which is followed by Prod Release 1.2.3. All three describe the same release, at increasing levels of confidence.
  • X.Y.Z increments only when the software changes, in accordance with Section 6. Dev RC 1.2.3 that gains backward-compatible functionality becomes Dev RC 1.3.0 before it becomes Prod Release 1.3.0.
  • The [Status] of a given [Branch] MUST NOT regress for the same X.Y.Z. A build that has reached Release 1.2.3 MUST NOT later be published as Beta 1.2.3 on the same branch. Returning to an earlier maturity level therefore requires incrementing X.Y.Z first.
  • The [Build] component is independent of status and MUST NOT be used to distinguish two builds of the same branch, status, and X.Y.Z. Two such builds MUST NOT be published as the same ZFVM version.

Correction from V2.0: V2.0 said nothing about this. V2.1 initially required X.Y.Z to be incremented on transition to Release, on the reasoning that maturity and version number should move together. That was wrong, and it was corrected in the V2.1 draft published after review. SemVer already resolves the same question for pre-releases: 1.0.0-alpha.1 is followed by 1.0.0-beta, by 1.0.0-rc.1, and finally by 1.0.0, all sharing one number. ZFVM's [Status] is the pre-release axis and X.Y.Z is the release axis, so they must not both advance. Requiring an artificial bump at release time also defeats the purpose of the status ladder, which exists precisely to let a version be tested at increasing levels of confidence before anything about the number changes.

The practical consequence is that a status ladder for one release looks like this:

Dev Pre-Alpha 0.3.0        (first runnable build)
Dev Alpha 0.3.0            (core functionality in place)
Dev Beta 0.3.0             (feature complete)
Dev RC 0.3.0               (candidate for release)
Prod Release 0.3.0         (released, still unstable API per Section 6.1)

and the next release continues from wherever the code actually changed:

Dev Beta 0.4.0             (backward-compatible functionality added)
Dev RC 0.4.0
Prod Release 0.4.0

8. Branch and Status Combinations

While the format permits any combination of the defined [Branch] and [Status], certain combinations are meaningful and others are contradictory.

  • Prod SHOULD typically carry the Release status. Because of Section 6.1, it MUST carry a status of RC or below when X is 0.
  • Dev MAY use any status from Pre-Alpha through RC while X is 0, and any status including Release once X is 1 or above.
  • Spec and Pub are testing environments and MAY use any status from Pre-Alpha through RC. Spec or Pub with the Release status SHOULD be avoided, since Release denotes stability rather than maturity, and these branches exist to host unstable builds.
  • Using a contradictory combination, such as Prod Pre-Alpha 1.0.0, SHOULD be avoided as it invites confusion.

Correction from V2.0: V2.0 Section 4 stated that Dev should use Pre-Alpha through RC, while V2.0 Section 6 used Dev Release as its worked example of cross-branch precedence. These contradicted each other. V2.1 resolves this: Release is valid on any branch and the recommendation in this section is advisory only, not a restriction.

9. Version Forms

For flexibility, ZFVM defines several representations tailored to different use cases. The Canonical Full Form is normative. The remaining forms are lossy projections of it, and each form states what information it drops.

9.1. Canonical Full Form

[Branch] [Status] [X.Y.Z] [Build]

The complete, unambiguous representation. This is the RECOMMENDED form for git tag annotations, release announcements, changelogs, and any context where full traceability is required.

Because the string contains spaces, it is awkward for identifiers that forbid them. Where a machine-readable identifier is required, the Symbolic Form in Section 9.4 SHOULD be used instead.

9.2. Contextual Form

[Status] [X.Y.Z] [Build]

This form omits the [Branch]. It is valid only where the branch is already unambiguous from context, such as within a CI/CD pipeline pinned to one branch, or in logs for a single environment. It MUST NOT be used where the branch is not otherwise known, because it cannot be parsed back into a complete ZFVM version.

9.3. Semantic Form

X.Y.Z

A bare Semantic Version number. This form asserts only API compatibility and deliberately carries no branch or status information. It is intended for dependency manifests and for interoperability with tools that parse SemVer.

Clarification from V2.0: V2.0 defined a bare 1.2.3 as both the Semantic Form and, independently, as shorthand for Prod Release 1.2.3. A parser could not distinguish these. V2.1 resolves the conflict in favour of the Semantic Form: a bare SemVer string is never interpreted as a ZFVM version. To assert a ZFVM production release, use Prod Release 1.2.3, the Symbolic Form, or the Shorthand Form v1.2.3.

9.4. Symbolic Form

Three symbolic forms are defined. All omit the [Build] component.

Option A (acronym, RECOMMENDED): <branch-acronym>-<status-acronym>-<X.Y.Z>

  • Uses acronyms for readability.
  • Examples: D-B-1.2.3 for Dev Beta 1.2.3, P-R-2.0.0 for Prod Release 2.0.0, D-PA-0.1.0 for Dev Pre-Alpha 0.1.0.
  • Preferred for container image tags, file names, and URL slugs, because every token is unambiguous and free of internal hyphens.

Option B (long name): <branch>-<status>-<X.Y.Z>

  • Uses the full identifiers, hyphen-separated.
  • Examples: Prod-Release-2.0.0 for Prod Release 2.0.0, Dev-Beta-1.2.3 for Dev Beta 1.2.3.
  • Widely used in release tooling because it mirrors the canonical form directly.
  • Ambiguous to parse by splitting on - when the status is Pre-Alpha, per the grammar note in Section 5.

Option C (legacy index): b<n>s<m>-<X.Y.Z>

  • Uses 1-based indices into the tables in Sections 12 and 13.
  • Examples: b2s3-1.2.3 for Dev Beta 1.2.3, b1s5-2.0.0 for Prod Release 2.0.0.
  • Deprecated in V2.1. See the stability constraint below.

An organisation SHOULD choose one symbolic form and use it consistently.

Stability constraint: Option C encodes table positions as data. It is therefore only safe while the vocabularies in Sections 12 and 13 are frozen. Future versions of this specification will not renumber existing entries; any addition will be appended. Consumers relying on Option C MUST pin the specification version they were generated against, and SHOULD migrate to Option A, which is unaffected by table changes.

9.5. Shorthand Form

For common production releases on the Prod branch:

  • v<X> implies Prod Release <X>.0.0, for example v1 implies Prod Release 1.0.0.
  • v<X.Y> implies Prod Release <X.Y>.0, for example v1.2 implies Prod Release 1.2.0.
  • v<X.Y.Z> implies Prod Release <X.Y.Z>, for example v1.2.3 implies Prod Release 1.2.3.

The Shorthand Form is defined only for the Prod branch with the Release status, and only for X greater than 0 in accordance with Section 6.1. There is no shorthand for a build, a non-production branch, or a pre-release status; use the Symbolic Form for those.

The v prefix is a display convention carried over from SemVer tradition. SemVer is explicit that v1.2.3 is not a semantic version and that 1.2.3 is the version while v1.2.3 is a tag name. ZFVM adopts the same convention here: the v is part of the shorthand notation, not part of the version value, and a producer SHOULD NOT persist it as an identifier distinct from the equivalent full version.

Note also that the ZFVM shorthand v1.2.3 and a git tag named v1.2.3 are different things that happen to look alike. The former is ZFVM shorthand for Prod Release 1.2.3. The latter is whatever the project chose to name a ref, and carries no meaning of its own.

This form is useful for public-facing version numbers, marketing materials, and quick references, where the reader can be assumed to know that the product is in production.

10. Mapping to Semantic Versioning

The Canonical Full Form is not a valid SemVer string. This section defines a normative projection from ZFVM to SemVer for dependency manifests and tooling that only accepts SemVer.

The projection is:

  • The [Status] component maps to the SemVer pre-release identifier, using the numeric ordinal from the table below. A Release status produces no pre-release identifier.
  • The [Build] component maps to SemVer build metadata, joined to the core version with +. Any _ in the [Build] identifier MUST be replaced with -, and each . becomes a SemVer identifier separator.
  • The [Branch] component has no representation in SemVer and MUST be carried out of band. This is an unavoidable limitation of the projection, not an omission in ZFVM.
StatusOrdinalProjected pre-release
Pre-Alpha0-0
Alpha1-1
Beta2-2
RC3-3
Release4none

The ordinal is a numeric SemVer identifier, and this is deliberate. SemVer compares pre-release identifiers numerically before it compares them lexically, and its own specification gives 1.0.0-0.3.7 as a valid example, so numeric pre-releases are fully sanctioned.

A readable alias would appear simpler, but it silently breaks precedence. SemVer orders the identifiers lexically in ASCII, which gives alpha < beta < rc correctly by accident of the alphabet, yet places pre-alpha after beta, because p sorts later than b. The order ZFVM needs is Pre-Alpha < Alpha < Beta < RC < Release, which no purely alphabetic mapping can express. The numeric ordinal guarantees that SemVer precedence reproduces the ZFVM status precedence defined in Section 11.

Worked examples:

ZFVM Canonical FormProjected SemVer
Prod Release 2.0.02.0.0
Prod Release 1.2.3 4561.2.3+456
Dev Beta 1.2.31.2.3-2
Dev Beta 1.2.3 4561.2.3-2+456
Dev Pre-Alpha 0.1.00.1.0-0
Dev RC 1.2.3 11201.2.3-3+1120
Pub Alpha 0.8.0 a7d3c1f0.8.0-1+a7d3c1f
Spec Beta 1.4.2 nightly_build1.4.2-2+nightly-build

Four consequences follow for consumers.

SemVer build metadata is ignored in precedence comparisons, so two ZFVM versions differing only in [Build] are equal under SemVer precedence. This is required, and matches the ZFVM rule in Section 11 that [Build] does not affect precedence. It is also why the [Branch] cannot be smuggled into build metadata: Debian hit exactly this wall and introduced an epoch prefix to work around it, because metadata is ignored by precedence rules. ZFVM has no such difficulty, since [Branch] occupies a visible prefix.

Because [Branch] is not represented, two ZFVM versions on different branches can project to the same SemVer string. Dev Release 1.2.3 and Pub Release 1.2.3 both project to 1.2.3. This is expected. A consumer that cares about the branch MUST store it separately.

Ordinal projections MUST NOT be compared against readable aliases. SemVer gives numeric identifiers lower precedence than non-numeric ones, so 1.2.3-2 sorts below 1.2.3-beta even though ZFVM assigns Beta the ordinal 2. Every ZFVM version MUST be projected consistently, or the resulting comparison is meaningless.

The projection is not round-trippable. Recovering the [Branch] from a SemVer string is impossible, and recovering the [Status] requires knowing which convention was used. A tool that stores only the projected string has discarded information ZFVM considers significant, so consumers SHOULD store both.

Some organisations encode the branch in the SemVer pre-release instead, for example 1.2.3-dev.2 or 1.2.3-beta.dev. This is a project-specific convention, is not defined by ZFVM, and MUST NOT be presented as a ZFVM projection.

11. Version Precedence

Two ZFVM versions are comparable only when they share the same [Branch]. Comparison across different branches is not defined by this specification, because a build on Dev is not meaningfully "greater than" or "less than" a build on Pub.

Where two versions share a branch, they are compared as follows:

  • The X.Y.Z components MUST be compared first, using SemVer precedence. This determines the ordering whenever X.Y.Z differs.
  • If X.Y.Z is identical, the [Status] components MUST be compared using the ordinal defined in Section 10: Pre-Alpha < Alpha < Beta < RC < Release. This ordering follows directly from the status ladder in Section 7 and cannot be inverted by a project.
  • The [Build] component MUST NOT affect precedence. It exists for traceability only.

A comparator MUST treat a non-zero build metadata value as semantically identical to its absence, in accordance with SemVer.

A comparator MUST NOT be used to decide whether one deployment may replace another on a different branch. Such decisions belong to the deployment system, not to ZFVM.

Worked example, all on the Dev branch. Reading left to right, the ordering is strictly increasing:

Dev Pre-Alpha 0.9.0
Dev Pre-Alpha 0.9.0 456          equal in precedence to the line above
Dev Pre-Alpha 1.0.0
Dev Alpha 1.0.0
Dev Beta 1.0.0
Dev Beta 1.0.1
Dev Beta 1.1.0
Dev RC 1.1.0
Dev RC 1.1.0 1120                equal in precedence to the line above
Dev Release 1.1.0
Dev RC 1.2.0
Dev Release 1.2.0

Note the two consequences that surprise people. First, X.Y.Z is compared before [Status], so Dev RC 1.2.0 sorts above Dev Release 1.1.0, even though Release is the higher status. Second, a pre-release of a higher version outranks a release of a lower one. This is exactly how SemVer behaves, and it is why 1.0.0-rc.1 precedes 1.0.0 but 1.1.0-rc.1 follows it.

Because X.Y.Z is compared before [Status], the status ladder in Section 7 produces a strictly increasing sequence only while X.Y.Z is held constant. The moment the code changes and X.Y.Z moves, the new pre-release correctly outranks every earlier release regardless of status.

12. Branch Identifiers

Each branch represents a specific development environment or purpose. The order of this table is significant only for the deprecated Symbolic Form C in Section 9.4.

IndexBranchAcronymFull NamePurpose
1ProdPProductionCode deployed to end users
2DevDDevelopmentActive development codebase
3SpecSSpecialTesting environments for specific features
4PubPbPublicDevelopment code deployed for public testing

The Spec branch denotes Special testing environments, not specifications.

The branch vocabulary is closed. An organisation that requires additional environments, such as a hotfix line, a canary line, or a per-customer build, SHOULD encode that distinction in the [Build] component or in its own release metadata rather than inventing a [Branch] value, because a non-standard [Branch] is not a valid ZFVM version.

13. Status Levels

Status indicates the maturity of the codebase. The order of this table defines the status ordinal referenced in Sections 10 and 11, and is significant only for the deprecated Symbolic Form C in Section 9.4.

IndexStatusAcronymOrdinalDescription
1Pre-AlphaPA0Initial development stage, extremely unstable
2AlphaA1Early development with core functionality implemented
3BetaB2Feature complete but may contain known issues
4RCRC3Release candidate, final testing before release
5ReleaseR4Stable, production-ready version

14. Validity Examples

Valid Canonical Full Forms:

VersionMeaning
Dev Beta 1.2.3 456Development branch, beta status, build 456
Prod Release 2.0.0Production release without a build identifier
Pub Alpha 0.8.0 a7d3c1fPublic testing branch, alpha status, commit hash
Spec Pre-Alpha 0.1.0Special test environment, earliest stage
Dev RC 1.2.3 20240521-1430Release candidate from a timestamped build
Spec Beta 1.4.2 nightly_buildSpecial test environment; _ is legal in ZFVM (Section 4.4)

Valid alternative forms:

FormVersionMeaning
ContextualBeta 1.2.3Branch known from context
Semantic1.2.3API compatibility only
Symbolic AD-B-1.2.3Dev Beta 1.2.3
Symbolic BProd-Release-2.0.0Prod Release 2.0.0
Symbolic Cb2s3-1.2.3Dev Beta 1.2.3
Shorthandv1, v1.2, v1.2.3Prod Release equivalents

Invalid:

VersionReason
Development Beta 1.2.3Full name used instead of the Dev identifier (Section 3)
dev beta 1.2.3Identifiers are case-sensitive (Section 2)
Prod Beta 1.2.3Trailing space
Prod Beta 1.02.3Leading zero in the minor version
Prod Release 0.9.0Release status with a zero major version (Section 6.1)
Prod BetaMissing the X.Y.Z component
Main Beta 1.2.3Main is not a defined branch (Section 12)
Prod Beta 1.2.3 build 456[Build] contains spaces (Section 4.4)
Prod Beta 1.2.3 456 789More than one [Build] component
Prod Beta 1.2.3 a7d3c1f#9[Build] uses a character outside the permitted set (Section 4.4)
Prod Beta 1.2.3 release/9[Build] uses a character outside the permitted set (Section 4.4)

The last two rows illustrate a distinction that is easy to get wrong: an underscore is valid in a ZFVM [Build] identifier but not in SemVer, whereas # and / are invalid in both. Section 4.4 permits _ and Section 10 specifies its translation on projection.

15. Implementation Guidance

For New Releases

  • Determine the type of change (major, minor, or patch) and increment X.Y.Z accordingly, per Section 6.
  • If the maturity level has changed, update the [Status] per Section 7. This does not itself increment X.Y.Z.
  • Choose the appropriate [Branch] per Section 12.
  • Optionally include a [Build] identifier from the build system, per Section 4.4.
  • Tag the repository with the Canonical Full Form, for example git tag -a "Prod Release 1.5.1 1120" -m "...". Because the canonical form contains spaces, also publish a machine-readable tag using Symbolic Form A, such as P-R-1.5.1, if your tooling requires space-free identifiers.
  • Project the version to SemVer per Section 10 before publishing to a package registry.

For Tool Integration

  • Use the Canonical Full Form for git tags, changelogs, and official documentation
  • Use the Contextual Form only where the branch is already unambiguous
  • Use the Semantic Form for dependency manifests, and record the branch separately
  • Use Symbolic Form A for container tags, file names, and URL slugs
  • Use the Shorthand Form for public-facing version numbers
  • Reject any string that does not match the grammar in Section 5 rather than attempting a repair

For Build Identifiers

  • CI/CD systems: use run numbers or job identifiers
  • Version control: use commit hashes, short or full
  • Nightly builds: use timestamps, or a stable literal such as nightly
  • Hotfixes: use descriptive identifiers such as hotfix-123

Validating a Version String

An implementation SHOULD validate in this order:

  • Reject the string if it contains any character outside %x20-7E
  • Split the Canonical Full Form on single spaces and confirm the field count is 3 or 4
  • Match the first field against the branch vocabulary, case-sensitively
  • Match the second field against the status vocabulary, case-sensitively
  • Match the third field against the SemVer core grammar
  • If a fourth field is present, match it against the [Build] character set and enforce the length guidance

A lenient parser is a common source of version confusion in this space. If a version string does not validate, it SHOULD be treated as an unknown version rather than coerced to something plausible.

16. Visualization

Branch: [Prod|Dev|Spec|Pub] → Status: [Pre-Alpha → Alpha → Beta → RC → Release] → Version: X.Y.Z → Build: [ID]
                                                                                           ↑ ↑ ↑
                                                                                    Major ─┘ │ │
                                                                                    Minor ───┘ │
                                                                                    Patch ─────┘

Status climbs at a fixed X.Y.Z; the number moves when the code changes:

  Dev Pre-Alpha 1.4.0  →  Dev Alpha 1.4.0   →  Dev Beta 1.4.0  →  Dev RC 1.4.0  →  Prod Release 1.4.0
  Dev Beta 1.5.0       →  Dev RC 1.5.0      →  Prod Release 1.5.0

Form mapping:  Prod Release 1.5.1 1120
            ↔  Symbolic A   P-R-1.5.1
            ↔  Symbolic B   Prod-Release-1.5.1
            ↔  Shorthand    v1.5.1
            ↔  SemVer        1.5.1+1120

17. Changes in Version 2.1

Normative changes since V2.0. Changes marked breaking change the meaning of, or the validity of, a previously valid string.

  • Breaking: Removed the rule that a bare X.Y.Z implies Prod Release X.Y.Z. A bare SemVer string now always means the Semantic Form. The shorthand for a full production version is now v<X.Y.Z> (Section 9.3, Section 9.5).
  • Breaking: The Canonical Full Form now requires the short branch identifier. Development Beta 1.2.3 is no longer valid; it is Dev Beta 1.2.3 (Section 3).
  • Breaking: Release status is no longer valid with a zero major version (Section 6.1).
  • Breaking: X.Y.Z no longer increments on transition to Release. A version climbs the status ladder at a fixed number, mirroring how SemVer moves from 1.0.0-rc.1 to 1.0.0. The number advances only when the software changes (Section 7).
  • Added Section 1, defining conformance obligations for parsers, producers, comparators, and mappers.
  • Added Section 2, defining the character set, case sensitivity, separator rules, and overall length guidance.
  • Added Section 5, an ABNF grammar covering every form.
  • Added Section 6.1, defining the zero-major-version interaction with [Status].
  • Added Section 7, defining how X.Y.Z changes when the [Status] changes, and prohibiting status regression.
  • Restored three SemVer requirements that V2.0 omitted: that a published version is immutable, that deprecating public API functionality increments the minor version, and that each element increases numerically rather than lexically (Section 6).
  • Resolved the V2.0 conflict between Section 4 and Section 6 over whether Dev Release is valid. It is valid; the branch recommendation is advisory (Section 8).
  • Added Section 10, a normative projection from ZFVM to SemVer using numeric status ordinals so that SemVer precedence preserves ZFVM status precedence. The projection now also specifies how to translate a [Build] identifier containing _, which is legal in ZFVM but not in SemVer.
  • Added a worked precedence example covering build-insensitive comparison, X.Y.Z ordering ahead of [Status], and pre-releases of higher versions outranking releases of lower ones (Section 11).
  • Rewrote precedence in Section 11 as a defined comparison for same-branch versions, and made cross-branch comparison explicitly undefined rather than context-dependent.
  • Added Symbolic Form B, the hyphenated long-name form already in common use in release tooling.
  • Deprecated Symbolic Form C and documented the stability constraint that motivated the deprecation (Section 9.4).
  • Narrowed the Shorthand Form to the Prod and Release branch and status combination (Section 9.5).
  • Added Section 14, with a table of valid and invalid version strings.
  • Added Section 17, this changelog.
  • Added Appendix A, situating ZFVM against zero-major versioning, Epoch SemVer, Debian epochs, and RomVer.
  • Restricted the [Build] character set to ASCII letters, digits, -, ., and _ (Section 4.4).
  • Relicensed the specification text from Apache-2.0 to CC BY-SA 4.0. See the License section.

Note for implementers

Between V2.0 and V2.1, Spec retains its meaning of Special and its acronym S. No branch identifier, acronym, or table index was changed, so Symbolic Form C strings generated against V2.0 remain valid under V2.1.

18. FAQ

Q1: What's new in ZFVM V2.1?

V2.1 makes ZFVM implementable rather than merely descriptive. It adds an ABNF grammar, a conformance section, a normative mapping to SemVer, explicit rules for status transitions, a table of valid and invalid strings, and a changelog. It also resolves three contradictions in V2.0, described in Section 17. See Section 17 for the full list.

Q2: How does ZFVM relate to Semantic Versioning (SemVer)?

ZFVM incorporates SemVer for the X.Y.Z part of its version string. It extends SemVer by prefixing [Branch] and [Status] and optionally suffixing [Build], which SemVer alone does not express. Section 10 defines how to project a ZFVM version back onto a SemVer string for dependency tooling.

Q3: When should I use the shorthand form?

Use the shorthand form for:

  • Public-facing version numbers on websites or in marketing materials
  • Simple production releases where the reader can be assumed to know the product is in production
  • Quick references in documentation

Avoid the shorthand when:

  • Full traceability is required
  • The build, branch, or status is anything other than Prod and Release
  • You are working in a development or testing environment

Q4: Can I mix symbolic form options?

While all three symbolic forms are valid, an organisation SHOULD choose one and use it consistently. Option A, the acronym form, is RECOMMENDED because every token is unambiguous. Option C, the index form, is deprecated and depends on table positions remaining frozen.

Q5: How do I handle version precedence across different branches?

You should not. ZFVM does not define precedence across branches, and no tool should pretend otherwise. Compare versions within a branch, and leave cross-branch deployment decisions to your deployment system.

Q6: What build identifier format should I use?

Choose based on your needs:

  • CI/CD run numbers: best for automated builds and traceability
  • Commit hashes: best for exact source code traceability
  • Timestamps: best for time-based releases such as nightly builds
  • Custom identifiers: best for special builds or hotfixes

All four fit within the character set this specification requires.

Q7: How does the shorthand form work with version precedence?

Shorthand forms are equivalent to their full forms for precedence:

  • v1 equals Prod Release 1.0.0
  • v1.2 equals Prod Release 1.2.0
  • v1.2.3 equals Prod Release 1.2.3

Since all three resolve to the Prod branch, standard SemVer precedence applies between them: 1.0.0 < 1.1.0 < 1.2.0 < 1.2.3.

Q8: When should I change the [Status], and what happens to X.Y.Z?

Change the status as the codebase matures:

  • Pre-Alpha to Alpha: core functionality implemented
  • Alpha to Beta: feature complete, entering the testing phase
  • Beta to RC: ready for release candidate testing
  • RC to Release: stable and production-ready

A status change does not increment X.Y.Z. The number advances only when the software changes, in accordance with Section 6. Section 7 works through a full example.

Q9: Why are the SemVer projections numbered instead of named?

Because SemVer compares pre-release identifiers numerically before comparing them lexically. Using -beta and -alpha happens to sort correctly, but adding -pre-alpha would sort incorrectly relative to -beta. The numeric ordinal in Section 10 guarantees that SemVer precedence reproduces ZFVM status precedence. SemVer's own specification lists 1.0.0-0.3.7 among its valid examples, so numeric pre-releases are fully sanctioned.

Q10: Can I add my own branch or status?

Not within a conforming implementation. The vocabularies in Sections 12 and 13 are closed, and a value outside them is not a valid ZFVM version. Encode additional distinctions in the [Build] component or in your own release metadata instead.

Q11: Why does my git tag not accept spaces?

Git does accept spaces, but many tag registries, container runtimes, and package registries do not. This is why the Canonical Full Form recommends a parallel Symbolic Form tag, as described in Section 15.

Q12: Does ZFVM support an epoch, in the sense of Epoch SemVer or Debian?

Not as a separate component, and it does not need one. Epoch SemVer introduces a second breaking-change tier by packing EPOCH and MAJOR into one number. ZFVM already separates those two tiers: [Branch] carries the major announcement, such as a move from Dev to Prod, while a major increment within a branch carries the technical break. Appendix A covers this and the related schemes in detail.

Q13: Why can I not store the branch in the SemVer build metadata?

Because SemVer precedence ignores build metadata, so anything placed there is invisible to comparison. Debian introduced an epoch prefix for precisely this reason. Since ZFVM carries [Branch] as a visible prefix, it has no such constraint. Section 10 explains the consequences.

Appendix A: Relationship to Other Versioning Schemes

This appendix is informative. It positions ZFVM against schemes it is often confused with, and records why ZFVM makes the choices it does. Nothing here is normative.

Zero-major versioning

A number of widely used projects, including React Native and sharp, ship indefinitely at 0.x.x. SemVer permits this: it defines 0.y.z as initial development in which anything may change and the API should not be considered stable. In practice this became a way to ship breaking changes indefinitely without ever declaring the software stable, at the cost of leaving consumers unable to reason about upgrade safety.

ZFVM takes the opposite position. Section 6.1 forbids the Release status while X is 0, which means 0.y.z can never be declared stable, and reaching stability requires an explicit 1.0.0. That is a deliberate constraint. A project that wants a long 0.x runway should say so with [Branch] and [Status] rather than by holding the major version at zero forever.

Epoch SemVer

Epoch SemVer, proposed by Anthony Fu, addresses a perceptual problem with SemVer: humans read version numbers on a logarithmic scale, so v2.0 to v3.0 feels like a catastrophe while v125.0 to v126.0 feels trivial, even though both are one major increment. The result is that maintainers hesitate to bump major, and breaking changes accumulate into a single enormous release. The Epoch SemVer proposal packs two counters into one number, {EPOCH * 1000 + MAJOR}.MINOR.PATCH, so that EPOCH carries the major announcement and MAJOR carries the technical break, without breaking any existing tooling.

ZFVM does not adopt Epoch SemVer's encoding, for a structural reason worth stating plainly: the [Branch] component already occupies the role that EPOCH occupies. A move from Dev to Prod is ZFVM's big announcement. A major version increment within a branch is the technical break. That is the same two-tier distinction, carried in a place where it is human-readable rather than arithmetically encoded, and it requires no multiplier to decode.

ZFVM does adopt the underlying principle, which is progressiveness. Section 7 is written around it: X.Y.Z advances one increment at a time, and the status ladder lets a version accumulate confidence at a fixed number before anything changes. A project that additionally wants two tiers of breaking change within a branch MAY layer Epoch SemVer's technique onto X.Y.Z. That is a project decision and is not defined here.

Debian epochs

Debian's package versions carry an epoch, written 1:2.3.4-1, which increments when the package must break its version ordering entirely. The name is where Epoch SemVer got its inspiration.

Epochs are relevant to ZFVM because they demonstrate a constraint ZFVM runs into directly: SemVer build metadata cannot carry ordering information, because precedence rules ignore it. A ZFVM implementation therefore cannot stash the branch in build metadata and expect it to participate in comparisons. The branch needs a visible prefix, which is exactly what [Branch] provides. Debian needed an epoch because it had no place to put this; ZFVM does.

RomVer

RomVer proposes HUMAN.MAJOR.MINOR, reserving the first position for a version number chosen for marketing appeal rather than for technical ordering. It shares Epoch SemVer's goal of separating announcement from substance.

ZFVM's analogue is the Shorthand Form in Section 9.5, which permits v1.2 and v1 to stand for Prod Release 1.2.0 and Prod Release 1.0.0 in public-facing material. The difference is that RomVer bakes the human number into the version itself, while ZFVM treats it as a lossy display projection that MUST NOT be used where machine parsing occurs.

Summary

SchemeWhat it addsRelationship to ZFVM
SemVer 2.0.0X.Y.Z, pre-releases, build metadataEmbedded as the X.Y.Z component; projected back per Section 10
Zero-majorAn unbounded 0.x runwayDisallowed from carrying Release; see Section 6.1
Epoch SemVerA second breaking-change tierBranch serves this role; principle adopted, encoding not
Debian epochEscape hatch for broken orderingIllustrates why branch must be a visible prefix, not metadata
RomVerA human-facing leading numberResembles the Shorthand Form, which stays a display-only projection

About

The Zillowe Foundation Versioning Method (ZFVM) was developed to provide a clear, comprehensive, and adaptable system for software versioning. ZFVM V2.1 builds upon the foundation of the original specification with a formal grammar, an implementable conformance model, a normative SemVer projection, and resolved ambiguities.

ZFVM extends Semantic Versioning (SemVer) to include critical context about the development branch and the maturity status of the software, addressing common challenges in managing complex software lifecycles.

ZFVM is designed and maintained by the Zillowe Foundation.

License

The text of this specification, including all tables, examples, and diagrams, is licensed under the Creative Commons Attribution-ShareAlike 4.0 International Public License.

The licence covers the specification text only. It does not grant any rights to the ZFVM name, acronym, logos, or other trademarks of the Zillowe Foundation, and it does not apply to software that implements ZFVM. Implementations remain subject to their own licences.

Suggested attribution:

Zillowe Foundation Versioning Method (ZFVM), Specification Version 2.1, by the Zillowe Foundation, licensed under CC BY-SA 4.0.

This replaces the Apache-2.0 licence that applied to the V2.0 text. The licence change applies to the specification text going forward and does not revoke rights already granted under the previous licence.


A software organization

2026 © All Rights Reserved.

  • All the content is available under CC BY-SA 4.0, expect where otherwise stated.

Last updated on