Files
StarPunk/docs/decisions/ADR-008-versioning-strategy.md
2025-11-18 19:21:31 -07:00

14 KiB

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

__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

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:

## [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:
    __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

Examples from Python Ecosystem

Tools

Internal Documentation


ADR: 008 Date: 2024-11-18 Status: Accepted Decision: Adopt Semantic Versioning 2.0.0 with PEP 440 compliance Supersedes: None