Skip to content

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:

  1. Dev — images are tagged with the git commit SHA (e.g., abc1234). No version number.
  2. 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.
  3. 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:

  1. Rename [Unreleased] to [X.Y.Z] - YYYY-MM-DD
  2. Add a new empty [Unreleased] section above it
  3. 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 orderLimitPerItemmaxItemQuantityPerOrder MINOR Field rename is additive (old field removed, new added) — but if API consumers depended on the old name, this could be MAJOR
Renamed maxItemQuantityPerOrdermaxQuantityPerDay + 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