# ADR-008: Versioning Strategy ## Status Accepted ## Context StarPunk is an IndieWeb CMS currently in active development, working toward its first production release. We need a comprehensive versioning strategy that: 1. **Communicates clearly** what type of changes each release contains 2. **Works with Python ecosystem** tools (pip, uv, PyPI compatibility) 3. **Aligns with IndieWeb values** of simplicity and sustainability 4. **Supports the project lifecycle** from development through maintenance 5. **Enables dependency management** for users who may build on StarPunk 6. **Provides predictability** for users about what to expect in updates ### Current State The project currently uses informal version terminology: - "V1" for the overall first release goal - "Phase 1.1", "Phase 1.2" for development milestones - "v1.1", "v2.0" for future iteration references This works for internal planning but lacks the precision needed for: - Public releases - Dependency management - Communicating breaking changes - Git tagging and release management ### Requirements 1. **Version number format** that indicates change severity 2. **Python ecosystem compliance** (PEP 440) 3. **Git workflow integration** (tagging, branching) 4. **Changelog format** for human-readable history 5. **Pre-release versioning** for alphas/betas if needed 6. **Upgrade communication** strategy 7. **Simplicity** appropriate for an indie project ## Decision We will adopt **Semantic Versioning 2.0.0 (SemVer)** with **Python PEP 440 compliance** for all StarPunk releases. ### Version Number Format **Structure**: `MAJOR.MINOR.PATCH[-PRERELEASE]` **Examples**: - `0.1.0` - Development version (Phase 1.1 complete) - `0.2.0` - Development version (Phase 1.2 complete) - `1.0.0` - First stable release (all V1 features complete) - `1.0.1` - Bug fix release - `1.1.0` - Feature release (backward compatible) - `2.0.0` - Major release (breaking changes) - `1.0.0a1` - Alpha pre-release (PEP 440 format) - `1.0.0b2` - Beta pre-release (PEP 440 format) - `1.0.0rc1` - Release candidate (PEP 440 format) ### Version Component Rules **MAJOR version** - Increment when making incompatible changes: - Breaking API changes - Database schema changes requiring migration - Configuration file format changes requiring user intervention - Removal of deprecated features - Major architectural changes **MINOR version** - Increment when adding functionality in backward-compatible manner: - New features - New API endpoints - Non-breaking enhancements - Optional new configuration parameters - Significant performance improvements **PATCH version** - Increment for backward-compatible bug fixes: - Bug fixes - Security patches - Documentation corrections - Minor performance improvements - Dependency updates (without feature changes) ### Development Phase (0.x.y) During development (pre-1.0), we use `0.MINOR.PATCH`: - MINOR increments for phase completions (Phase 1.1 → 0.1.0, Phase 1.2 → 0.2.0) - PATCH increments for bug fixes during development - Breaking changes are allowed without major version increment - Public API is not considered stable ### Git Tagging Convention **Format**: `vMAJOR.MINOR.PATCH[-PRERELEASE]` **Examples**: - `v0.1.0` - Development version tag - `v1.0.0` - Stable release tag - `v1.0.1` - Bug fix release tag - `v1.0.0-alpha.1` - Alpha pre-release tag (Git format) **Tag type**: Annotated tags (not lightweight) - Contains tagger, date, message - Can include release notes - Can be GPG signed ### Version Storage **Primary source of truth**: `starpunk/__init__.py` ```python __version__ = "1.0.0" __version_info__ = (1, 0, 0) ``` **Secondary locations**: - `pyproject.toml` - Package metadata (if used) - Git tags - Release markers - `CHANGELOG.md` - Human-readable history **Synchronization**: Manual for V1 (simple, no automation dependencies) ### Changelog Format **File**: `CHANGELOG.md` **Format**: Based on [Keep a Changelog](https://keepachangelog.com/) **Categories**: - Added - New features - Changed - Changes to existing functionality - Deprecated - Features that will be removed - Removed - Features that have been removed - Fixed - Bug fixes - Security - Security vulnerability fixes **Example**: ```markdown ## [1.0.0] - 2024-11-18 ### Added - IndieAuth authentication via IndieLogin - Micropub endpoint for publishing - RSS feed generation - File-based note storage ### Security - Implemented path traversal protection - Added CSRF protection for authentication flows ``` ### Pre-Release Versioning **Format**: PEP 440 compliant - Alpha: `1.0.0a1`, `1.0.0a2` - Beta: `1.0.0b1`, `1.0.0b2` - Release Candidate: `1.0.0rc1`, `1.0.0rc2` **Git tags use hyphen**: `v1.0.0-alpha.1` (for readability) **Python `__version__` uses PEP 440**: `1.0.0a1` (for pip compatibility) ### Phase-to-Version Mapping **Implementation phases** (internal planning) map to **version numbers** (public releases): ``` Phase 1.1 complete → Version 0.1.0 Phase 1.2 complete → Version 0.2.0 Phase 1.3 complete → Version 0.3.0 Phase 2.1 complete → Version 0.4.0 All V1 complete → Version 1.0.0 V1.1 features → Version 1.1.0 V2 features → Version 2.0.0 ``` **Clarification**: - "V1" refers to feature scope, not version number - Version 1.0.0 implements the "V1 feature set" - Phases are development milestones, versions are public releases ## Rationale ### Why Semantic Versioning? 1. **Industry Standard**: Used by Flask, Django, Requests, and most Python packages 2. **Clear Communication**: Version number immediately conveys impact of changes 3. **Predictable**: Users know what to expect from each version increment 4. **Dependency Management**: Works seamlessly with pip version specifiers 5. **Simple**: Easy to understand and apply without complex rules ### Why Not Calendar Versioning (CalVer)? CalVer (e.g., `2024.11.18`) was considered but rejected: **Pros**: - Shows when software was released - No ambiguity about version order **Cons**: - Doesn't communicate impact of changes (patch vs breaking change) - Less common in Python ecosystem - Doesn't help users assess upgrade risk - Overkill for indie project release cadence **Conclusion**: SemVer's semantic meaning is more valuable than date information ### Why Not ZeroVer (0.x forever)? Some projects stay at 0.x.y indefinitely to signal "still evolving". Rejected because: - Creates uncertainty about production readiness - Version 1.0.0 signals "ready for production use" - We have a clear 1.0 feature scope (V1) - Users deserve clarity about stability ### Why PEP 440 Compliance? **PEP 440** is Python's version identification standard: - Required for PyPI publication (if we ever publish) - Compatible with pip, uv, and all Python package managers - Slightly different pre-release format than SemVer (e.g., `1.0.0a1` vs `1.0.0-alpha.1`) - Used by all major Python frameworks **Decision**: Use PEP 440 format in Python code, SemVer-style in Git tags (Git tags are more flexible) ### Why Manual Version Management (V1)? Considered automation tools: - `bump2version` - Automates version bumping - `python-semantic-release` - Determines version from commit messages - `setuptools_scm` - Derives version from Git tags **Decision**: Manual for V1 because: 1. Simple - no extra dependencies 2. Full control - explicit about versions 3. Aligns with indie philosophy - minimal tooling 4. Can add automation later if needed ### Why Annotated Tags? **Annotated tags** vs lightweight tags: Annotated tags: - Contain metadata (tagger, date, message) - Can include release notes - Can be GPG signed - Treated as full objects in Git **Decision**: Always use annotated tags for releases ### Why CHANGELOG.md? **Changelog** provides human-readable release history: - Users can quickly see what changed - Easier to read than Git commits - Standard location (`CHANGELOG.md`) - Standard format (Keep a Changelog) - Can be generated from commits or written manually **Decision**: Maintain manually for V1 (precise control over messaging) ## Consequences ### Positive 1. **Clear Communication**: Users know exactly what each version means 2. **Ecosystem Compatibility**: Works with all Python tools 3. **Predictable Upgrades**: Users can assess risk before upgrading 4. **Professional Image**: Signals mature, well-maintained software 5. **Dependency Management**: Other projects can depend on StarPunk versions 6. **Git Integration**: Clean tagging and release workflow 7. **Flexible**: Can add automation later without changing format 8. **Standards-Based**: Uses established, documented standards ### Negative 1. **Manual Effort**: Requires discipline to update version in multiple places 2. **Coordination**: Must remember to update version, changelog, and tag 3. **Breaking Change Discipline**: Must carefully evaluate what constitutes breaking change 4. **Learning Curve**: Contributors need to understand SemVer rules ### Neutral 1. **0.x Signals Development**: May discourage early adopters who want "1.0" stability 2. **Commitment to Backward Compatibility**: Once at 1.0, breaking changes require major version 3. **Changelog Maintenance**: Ongoing effort to document changes ### Mitigations **For manual effort**: - Document clear release process in `versioning-strategy.md` - Create release checklist - Consider automation in V2+ if needed **For breaking change discipline**: - Deprecate features one version before removal when possible - Document breaking changes prominently - Provide upgrade guides for major versions **For 0.x concerns**: - Clearly communicate that 0.x is pre-production - Move to 1.0.0 once V1 features are complete and tested - Don't stay at 0.x longer than necessary ## Implementation ### Immediate Actions 1. **Set current version**: `0.1.0` (Phase 1.1 development) 2. **Create `starpunk/__init__.py`**: ```python __version__ = "0.1.0" __version_info__ = (0, 1, 0) ``` 3. **Create `CHANGELOG.md`** with `[Unreleased]` section 4. **Update README.md** with version information 5. **Tag current state**: `git tag -a v0.1.0 -m "Development version 0.1.0"` ### Release Workflow When ready to release: 1. Update `starpunk/__init__.py` with new version 2. Update `CHANGELOG.md` with release date 3. Commit: `git commit -m "Bump version to X.Y.Z"` 4. Tag: `git tag -a vX.Y.Z -m "Release X.Y.Z: [description]"` 5. Push: `git push origin main vX.Y.Z` ### Documentation - Create comprehensive `docs/standards/versioning-strategy.md` - Document examples, decision tree, and FAQ - Include upgrade guide template - Reference in README.md ### Future Enhancements **V2+ Considerations**: - Add `bump2version` for automation if manual process becomes tedious - Consider API versioning if need to support multiple incompatible API versions - Add database schema versioning if migrations become complex - Automated changelog generation from commit messages ## Alternatives Considered ### Alternative 1: Calendar Versioning (CalVer) **Format**: `YYYY.MM.PATCH` (e.g., `2024.11.0`) **Pros**: - Shows release date - Clear chronological order - Used by Ubuntu, PyCharm **Cons**: - Doesn't communicate change impact - Less common in Python web frameworks - Doesn't indicate breaking changes - Overkill for indie project cadence **Rejected**: Semantic meaning more important than date ### Alternative 2: Simple Integer Versioning **Format**: `1`, `2`, `3` (e.g., like TeX: 3.14159...) **Pros**: - Extremely simple - No ambiguity **Cons**: - No information about change severity - Doesn't work with Python dependency tools - Not standard in Python ecosystem - Too minimalist for practical use **Rejected**: Too simplistic, poor ecosystem fit ### Alternative 3: Modified SemVer (Django-style) **Format**: `MAJOR.FEATURE.PATCH` (e.g., Django's `4.2.7`) **Pros**: - Used by Django - Separates features from bug fixes **Cons**: - Non-standard SemVer interpretation - Confusing: when to increment feature vs major? - Doesn't clearly indicate breaking changes - Less predictable **Rejected**: Standard SemVer is clearer ### Alternative 4: ZeroVer (0.x forever) **Format**: Stay at `0.x.y` indefinitely **Pros**: - Signals "always evolving" - No commitment to stability **Cons**: - Users can't tell when production-ready - Doesn't signal stability improvements - Version 1.0.0 has meaning: "ready to use" **Rejected**: We have clear 1.0 goals, should signal when achieved ### Alternative 5: Hybrid SemVer + CalVer **Format**: `YYYY.MINOR.PATCH` (e.g., `2024.1.0`) **Pros**: - Combines date and semantic information **Cons**: - Unusual, confusing - Not standard - Year increments don't mean anything semantically **Rejected**: Combines worst of both approaches ## References ### Standards - [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) - Official SemVer specification - [PEP 440 - Version Identification](https://peps.python.org/pep-0440/) - Python version numbering standard - [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Changelog format standard - [Calendar Versioning](https://calver.org/) - CalVer specification (considered but not adopted) ### Examples from Python Ecosystem - [Flask Versioning](https://github.com/pallets/flask/releases) - Uses SemVer - [Django Release Process](https://docs.djangoproject.com/en/stable/internals/release-process/) - Modified SemVer - [Requests Versioning](https://github.com/psf/requests/releases) - Uses SemVer - [FastAPI Versioning](https://github.com/tiangolo/fastapi/releases) - Uses SemVer ### Tools - [bump2version](https://github.com/c4urself/bump2version) - Version bump automation - [python-semantic-release](https://python-semantic-release.readthedocs.io/) - Automated semantic releases - [setuptools_scm](https://github.com/pypa/setuptools_scm) - SCM-based versioning ### Internal Documentation - [Versioning Strategy](/home/phil/Projects/starpunk/docs/standards/versioning-strategy.md) - Complete versioning specification - [Architecture Overview](/home/phil/Projects/starpunk/docs/architecture/overview.md) - System architecture - [Development Setup](/home/phil/Projects/starpunk/docs/standards/development-setup.md) - Development workflow --- **ADR**: 008 **Date**: 2024-11-18 **Status**: Accepted **Decision**: Adopt Semantic Versioning 2.0.0 with PEP 440 compliance **Supersedes**: None