Versioning Guide¶
Smartsapp follows Semantic Versioning 2.0.0 (SemVer).
Version Format¶
MAJOR.MINOR.PATCH
│ │ └── Bug fixes, small tweaks (no new features, no breaking changes)
│ └──────── New features (backwards-compatible)
└────────────── Breaking changes (not backwards-compatible)
Example progression:
1.0.0 → Initial release
1.1.0 → Added feeding list export feature
1.1.1 → Fixed export date filter bug
1.2.0 → Added meal ticket management
2.0.0 → Restructured API endpoints (breaking change)
When to Bump Each Number¶
PATCH (1.0.x) — Bug fixes and minor tweaks¶
Bump PATCH when you:
- Fix a bug that didn't change the API or data model
- Fix a typo in an error message
- Improve performance without changing behaviour
- Update documentation
- Fix a broken deployment or CI config
The pipeline auto-increments PATCH on every staging promotion by default.
MINOR (1.x.0) — New features, backwards-compatible¶
Bump MINOR when you:
- Add a new API endpoint
- Add a new entity or field to an existing entity
- Add a new module (e.g., attendance, pickup management)
- Add a new optional query parameter or filter
- Add a new event topic
- Add support for a new integration (e.g., POS terminal, Sika ID)
Key rule: Existing API consumers should not break. Old requests still work the same way.
MAJOR (x.0.0) — Breaking changes¶
Bump MAJOR when you:
- Remove or rename an API endpoint
- Change the structure of an API response (remove fields, rename fields)
- Change the meaning of an existing field
- Remove a database column that external systems depend on
- Change authentication mechanism (e.g., legacy JWT → Keycloak)
- Change event message format in a way that breaks consumers
Key rule: Something that worked before will stop working after this change.
Decision Flowchart¶
Does this change break existing API consumers?
│
├── YES → MAJOR bump
│
└── NO → Does this change add new capabilities?
│
├── YES → MINOR bump
│
└── NO → PATCH bump
Pre-release Versions¶
For pre-release or development builds, use:
1.2.0-rc.1 Release candidate 1
1.2.0-beta.1 Beta release
These are not used in the current pipeline but may be adopted for staging validation.
How Versioning Works in the Pipeline¶
See CI/CD Pipeline for the full deployment flow. Summary:
- Dev — images are tagged with the git commit SHA (e.g.,
abc1234). No version number. - Staging promotion — the pipeline auto-creates a git tag (e.g.,
v1.14.3) by incrementing PATCH from the latest tag. The image is retagged with the version. - Production promotion — the same versioned image is deployed. No rebuild.
Overriding the version bump¶
To bump MINOR or MAJOR instead of PATCH, manually create the git tag before promoting:
# For a minor bump
git tag v1.15.0
git push origin v1.15.0
# For a major bump
git tag v2.0.0
git push origin v2.0.0
The pipeline will detect the existing tag and skip auto-incrementing.
Version in Code¶
The application version is defined in build.gradle.kts:
version = "0.0.1-SNAPSHOT"
This is the build version and stays as SNAPSHOT during development. The release version comes from git tags, not this field. Do not manually update build.gradle.kts version for releases.
Changelog and Versioning¶
When cutting a release, update CHANGELOG.md:
- Rename
[Unreleased]to[X.Y.Z] - YYYY-MM-DD - Add a new empty
[Unreleased]section above it - Commit the changelog update as part of the release
## [Unreleased]
## [1.15.0] - 2026-03-20
### Added
- Meal ticket management
- ...
Examples from This Project¶
| Change | Version Bump | Reason |
|---|---|---|
| Added legacy JWT auth | MINOR | New feature (auth integration) |
Added legacyUserId to SchoolStaff |
MINOR | New field on existing entity |
| Fixed broken doc links | PATCH | Bug fix, no feature change |
Renamed orderLimitPerItem → maxItemQuantityPerOrder |
MINOR | Field rename is additive (old field removed, new added) — but if API consumers depended on the old name, this could be MAJOR |
Renamed maxItemQuantityPerOrder → maxQuantityPerDay + cumulative per-day enforcement |
MAJOR | Field rename + behavior change: quantity is now enforced cumulatively across all non-cancelled orders for the same (student, item, scheduled date), not per single order line. A parent placing two separate orders on the same day can no longer bypass the cap. Old clients still sending maxItemQuantityPerOrder will have it silently ignored. |
| Future: Migrate from legacy JWT to Keycloak | MAJOR | Auth mechanism change, breaks existing tokens |