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

458 lines
14 KiB
Markdown

# 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