FOLIO Application Versioning Proposal

FOLIO Application Versioning Proposal

FOLIO Application Versioning Proposal

Use vMAJOR.minor.patch as a release-cadence version, similar to Keycloak and macOS, instead of reserving artificial version slots such as +100 for applications or +10 for modules. This keeps the version readable, predictable, and aligned with how FOLIO actually validates applications today.

vM.m.p MAJOR - Flower release or another large feature/release train. This does not imply breaking module interface changes. minor - CSP release. patch - Security fixes and urgent fixes for the current CSP.

Executive Summary

FOLIO is not a monolith. Application and module versions currently work mostly as artifact and release identifiers. They do not carry strong compatibility meaning by themselves.

The real compatibility checks are elsewhere:

Area

What is actually validated

Area

What is actually validated

Module interface integrity

Required interfaces must be satisfied by compatible provided interfaces.

Application dependencies

Dependency versions must satisfy SemVer ranges.

Descriptor syntax

Application and module versions must look like SemVer.

Upgrade flow

Requested application version must be higher than the currently entitled version.

Therefore, the version number should communicate the release train, not pretend to encode compatibility rules that are already handled by interface versions and dependency ranges.

Why Not Slot-Based +100 / +10 Versioning

The slot-based approach makes version numbers carry operational bookkeeping:

Application: 3.0.100 -> 3.0.101 -> 3.0.200 Module: 3.0.10 -> 3.0.11 -> 3.0.20

This creates several problems:

Problem

Impact

Problem

Impact

Artificial gaps

Numbers become harder to read and explain.

Different scales for apps and modules

Developers must remember two policies.

False precision

100 or 10 suggests validation semantics that the platform does not enforce.

Future constraints

Slots must be reserved, explained, and protected.

More policy than value

Compatibility is still validated by interfaces and dependency ranges.

A release-cadence model is simpler:

3.0.0 - Flower release / large feature train 3.1.0 - CSP 1 3.1.1 - urgent or security fix for CSP 1 3.2.0 - CSP 2 3.2.1 - urgent or security fix for CSP 2

Industry Precedent

Keycloak deliberately changed its versioning approach for continuous delivery. A major version can represent a feature release and does not necessarily mean a breaking change.

macOS also uses major versions to represent release generations. The version communicates the release line first, not API compatibility.

FOLIO can use the same idea: version numbers identify the release generation and patch level, while compatibility remains governed by module interfaces and dependency ranges.

Proposed Version Meaning

Version part

Meaning in FOLIO

Version part

Meaning in FOLIO

MAJOR

Flower release or another large coordinated feature/release train.

minor

CSP release within that major line.

patch

Security fix or urgent fix for the current CSP.

Example:

v3.0.0 - Trillium / major release train v3.1.0 - Trillium CSP 1 v3.1.1 - Security fix for Trillium CSP 1 v3.2.0 - Trillium CSP 2 v4.0.0 - Next Flower release or another large release train

Pros

Pro

Why it matters

Pro

Why it matters

Easier to understand

Versions are short and map directly to release cadence.

Matches current validation

Compatibility remains in interfaces and dependency ranges.

Avoids artificial slots

No need to reserve 10 or 100 ranges.

Same model for apps and modules

One versioning rule can be explained across layers.

Compatible with SemVer tooling

Still uses MAJOR.minor.patch syntax and ordering.

Better communication

A version tells people which release train and patch level they are looking at.

Similar to Keycloak and macOS

Proven pattern for release-generation versioning.

Opportunity for automated patch-releases

In future it provides possibility to implemented automated flows for each of the CSPs to identify outdated or security-vulnerable dependencies and update them as a patch release

Con

Con

Mitigation

Con

Mitigation

This is a small shift away from traditional SemVer because MAJOR does not always mean a breaking API change.

Document the rule clearly: interface versions define compatibility; application and module versions define release cadence.

Key Principle

Application and module versions should not be overloaded with compatibility meaning.

Compatibility should remain validated by:

Module requires/provides interfaces Application dependency SemVer ranges

The application or module version should answer:

Which FOLIO release train is this? Which CSP is this? Is this an urgent or security patch for that CSP?

Current Validation Reality

The current code in mgr-applications and mgr-tenant-entitlements supports this split of responsibilities:

Validation behavior

Current implementation meaning

Validation behavior

Current implementation meaning

Application/module versions

Validated for SemVer-like syntax and used for identity, ordering, and upgrade comparison.

Application dependencies

Validated by name and SemVer range satisfaction.

Module interfaces

Required interfaces must be compatible with provided interfaces.

Tenant upgrade

Requested application version must be greater than the installed version.

Important distinction
Application and module versions do not currently prove API compatibility by themselves. The real compatibility boundary is the module interface contract plus the application dependency range.

Diagram: Responsibility Split

image-20260429-123338.png

 

@startuml title FOLIO Versioning Responsibilities left to right direction rectangle "Application Version\nvM.m.p\nRelease train marker" as AppVersion rectangle "Module Version\nvM.m.p\nArtifact marker" as ModuleVersion rectangle "Interface Version\nmajor.minor\nCompatibility contract" as InterfaceVersion rectangle "Application Dependency\nname + SemVer range" as DependencyRange rectangle "mgr-applications" as AM { rectangle "Descriptor syntax checks" as Syntax rectangle "Dependency range validation" as DepValidation rectangle "Interface integrity validation" as InterfaceValidation } rectangle "mgr-tenant-entitlements" as MTE { rectangle "Entitlement / upgrade flow" as Flow rectangle "Installed vs requested app check" as UpgradeCheck } AppVersion --> Syntax : must be valid SemVer syntax ModuleVersion --> Syntax : must be valid SemVer syntax DependencyRange --> DepValidation : must satisfy SemVer range InterfaceVersion --> InterfaceValidation : requires must match provides AppVersion --> UpgradeCheck : must increase on upgrade InterfaceValidation --> Flow : compatible module set DepValidation --> Flow : compatible application set note bottom of AppVersion Does not define API compatibility by itself. end note note bottom of InterfaceVersion This is the real compatibility contract. end note @enduml

Diagram: Proposed Release Flow

image-20260429-123204.png

 

@startuml title Proposed FOLIO Application Version Flow skinparam shadowing false state "Flower / large feature train\nv3.0.0" as Major state "CSP 1\nv3.1.0" as Minor1 state "Urgent or security fix\nv3.1.1" as Patch1 state "Another urgent fix\nv3.1.2" as Patch2 state "CSP 2\nv3.2.0" as Minor2 state "Next Flower / large feature train\nv4.0.0" as NextMajor Major --> Minor1 Minor1 --> Patch1 Patch1 --> Patch2 Patch2 --> Minor2 Minor2 --> NextMajor note right of Major MAJOR follows release train, not necessarily breaking API change. end note note right of Minor1 minor identifies the CSP line. end note note right of Patch1 patch is for security or urgent fixes within the current CSP. end note @enduml

Diagram: Validation Flow

image-20260429-122940.png

 

@startuml title Application Validation Flow actor "Application Descriptor" as Descriptor participant "mgr-applications" as AM participant "DependenciesValidator" as DV participant "Interface Integrity Check" as IV participant "mgr-tenant-entitlements" as MTE Descriptor -> AM : register / validate descriptor AM -> AM : validate descriptor syntax\nid, version format, module descriptor shape AM -> DV : validate application dependencies DV -> DV : dependency name exists? DV -> DV : dependency version satisfies SemVer range? AM -> IV : validate module interfaces IV -> IV : collect required interfaces IV -> IV : collect provided interfaces IV -> IV : required interface compatible with provided interface? MTE -> AM : load application descriptors MTE -> AM : validate descriptor set MTE -> MTE : verify requested upgrade version is higher MTE -> MTE : build entitlement / upgrade flow note over AM, MTE Application/module version numbers are used for identity, ordering, and dependency range matching. They do not replace interface compatibility validation. end note @enduml

Diagram: Slot-Based vs Release-Cadence Versioning

image-20260429-122849.png
@startuml title Versioning Approach Comparison left to right direction rectangle "Slot-Based Versioning\n\nApps: 3.0.100 -> 3.0.101 -> 3.0.200\nModules: 3.0.10 -> 3.0.11 -> 3.0.20" as Slot rectangle "Release-Cadence Versioning\n\n3.0.0 -> 3.1.0 -> 3.1.1 -> 3.2.0" as Cadence rectangle "Requires reserved gaps\nDifferent rules by layer\nPolicy is not validated as compatibility" as SlotIssues rectangle "Readable release line\nSingle rule for apps/modules\nCompatibility remains in interfaces/ranges" as CadenceBenefits Slot --> SlotIssues Cadence --> CadenceBenefits @enduml

Summary

The recommended approach is to keep SemVer-shaped versions but define their meaning around the FOLIO release cadence.

This gives us simple, readable versions while preserving the validation model FOLIO already uses:

Versions communicate release lineage. Interfaces and dependency ranges validate compatibility.