Skip to content

๐Ÿ”„ Version Management - Evoluciรณn sin Ruptura

๐Ÿ“‹ Descripciรณn

El Version Management System gestiona la evoluciรณn de presets a travรฉs del tiempo, garantizando backward compatibility y permitiendo migraciรณn automรกtica entre versiones. Es el sistema que permite que presets antiguos funcionen en versiones nuevas del software.

Criticidad: โญโญโญโญโญ (Crรญtico - sin esto, los usuarios pierden sus presets al actualizar)


๐ŸŽฏ Objetivos

  1. Backward Compatibility: Garantizar que presets antiguos funcionen siempre
  2. Automatic Migration: Migrar presets automรกticamente a versiones nuevas
  3. Safe Rollback: Permitir rollback si la migraciรณn falla
  4. Compatibility Matrix: Tracking de quรฉ versiones son compatibles
  5. Deprecation System: Sistema de warnings para features deprecated

๐Ÿ—๏ธ Arquitectura

Componentes Principales

Version Management System
โ”‚
โ”œโ”€โ”€ Version System
โ”‚   โ”œโ”€โ”€ SemanticVersion (MAJOR.MINOR.PATCH)
โ”‚   โ”œโ”€โ”€ VersionParser (string โ†’ Version)
โ”‚   โ”œโ”€โ”€ VersionComparator (A < B, A == B)
โ”‚   โ””โ”€โ”€ VersionRange (>=1.0.0, <2.0.0)
โ”‚
โ”œโ”€โ”€ Migration System
โ”‚   โ”œโ”€โ”€ MigrationScript (v1.0 โ†’ v1.1)
โ”‚   โ”œโ”€โ”€ MigrationChain (v1.0 โ†’ v1.5 โ†’ v2.0)
โ”‚   โ”œโ”€โ”€ MigrationRegistry (script lookup)
โ”‚   โ””โ”€โ”€ MigrationExecutor (run + validate)
โ”‚
โ”œโ”€โ”€ Compatibility System
โ”‚   โ”œโ”€โ”€ CompatibilityMatrix (version โ†’ version map)
โ”‚   โ”œโ”€โ”€ CompatibilityChecker (can migrate?)
โ”‚   โ””โ”€โ”€ BreakingChanges tracker
โ”‚
โ””โ”€โ”€ Version Manager (orchestrator)
    โ”œโ”€โ”€ migrate(preset, target_version)
    โ”œโ”€โ”€ checkCompatibility(v1, v2)
    โ”œโ”€โ”€ canMigrate(preset, target_version)
    โ””โ”€โ”€ rollback(preset)

Semantic Versioning

MAJOR.MINOR.PATCH - MAJOR: Breaking changes (incompatible API) - MINOR: New features (backward compatible) - PATCH: Bug fixes (backward compatible)

Ejemplos: - 1.0.0 โ†’ 1.0.1: Patch (bug fix) - 1.0.1 โ†’ 1.1.0: Minor (new feature) - 1.1.0 โ†’ 2.0.0: Major (breaking change)


๐Ÿ”„ Migration Pipeline

1. Detection Phase

auto version = preset.getVersion();
auto current = VersionManager::getCurrentVersion();

if (version < current) {
    // Migration needed
}

2. Compatibility Check

bool can_migrate = manager.canMigrate(preset, current);

if (!can_migrate) {
    // Version too old or incompatible
    throw IncompatibleVersionError();
}

3. Migration Execution

// Build migration chain: v1.0 โ†’ v1.5 โ†’ v2.0
auto chain = manager.buildMigrationChain(version, current);

// Execute migrations
for (auto& migration : chain) {
    preset = migration->migrate(preset);
    preset.validate(); // Ensure validity at each step
}

4. Validation

// Verify migrated preset is valid
if (!preset.validate()) {
    // Rollback if validation fails
    preset = manager.rollback(preset);
}

๐Ÿ“ Migration Scripts

Script Structure

class MigrationScript {
public:
    Version from_version() const;
    Version to_version() const;

    PresetSchema migrate(const PresetSchema& preset) const;
    PresetSchema rollback(const PresetSchema& preset) const;

    std::string description() const;
    std::vector<std::string> breaking_changes() const;
};

Example Migration: v1.0 โ†’ v1.1

Changes: - Added new parameter filter.drive - Renamed osc.waveform โ†’ osc.wave_type - Deprecated env.curve (use env.shape instead)

class Migration_1_0_to_1_1 : public MigrationScript {
    PresetSchema migrate(const PresetSchema& preset) const override {
        PresetSchema migrated = preset;

        // Add new parameter with default value
        if (!migrated.hasParameter("filter.drive")) {
            migrated.setParameter("filter.drive", 1.0);
        }

        // Rename parameter
        if (migrated.hasParameter("osc.waveform")) {
            auto value = migrated.getParameter("osc.waveform");
            migrated.setParameter("osc.wave_type", value);
            migrated.removeParameter("osc.waveform");
        }

        // Deprecate parameter (keep for now)
        if (migrated.hasParameter("env.curve")) {
            auto value = migrated.getParameter("env.curve");
            migrated.setParameter("env.shape", value);
            // Mark as deprecated but don't remove
            migrated.addDeprecationWarning("env.curve", "Use env.shape instead");
        }

        // Update version
        migrated.setVersion({1, 1, 0});

        return migrated;
    }
};

๐Ÿงช Compatibility Matrix

Version Compatibility Table

From Version To Version Compatible? Migration Path Breaking Changes
1.0.0 1.0.1 โœ… Yes Direct None
1.0.0 1.1.0 โœ… Yes Direct None
1.0.0 2.0.0 โœ… Yes Via 1.5.0 Parameter renames
1.0.0 3.0.0 โŒ No - Too many changes

Compatibility Rules

Within MAJOR version: - โœ… Always compatible forward (1.0 โ†’ 1.9) - โœ… Usually compatible backward (1.9 โ†’ 1.0)

Across MAJOR versions: - โš ๏ธ Needs migration (1.x โ†’ 2.x) - โŒ May be incompatible (1.x โ†’ 3.x)


๐Ÿšจ Breaking Changes

Types of Breaking Changes

  1. Parameter Removal
  2. Parameter deleted completely
  3. Requires fallback or migration

  4. Parameter Rename

  5. Name changed
  6. Migration script can handle

  7. Parameter Type Change

  8. Type changed (float โ†’ int, string โ†’ enum)
  9. May require value conversion

  10. Parameter Semantics Change

  11. Meaning changed (range, units)
  12. Requires careful migration

  13. Structure Change

  14. Schema structure changed
  15. Complex migration needed

Handling Breaking Changes

class BreakingChange {
    enum Type {
        ParameterRemoved,
        ParameterRenamed,
        ParameterTypeChanged,
        ParameterSemanticsChanged,
        StructureChanged
    };

    Type type;
    std::string description;
    std::string mitigation;  // How to handle it
};

๐Ÿ“Š Usage Examples

Example 1: Load Old Preset

// Load preset from file
auto preset = PresetSchema::loadFromFile("old_preset.json");

// Check if migration needed
auto manager = VersionManager::getInstance();

if (manager.needsMigration(preset)) {
    std::cout << "Migrating from v" << preset.getVersion().toString()
              << " to v" << manager.getCurrentVersion().toString() << "\n";

    // Migrate automatically
    preset = manager.migrate(preset);

    // Save migrated version
    preset.saveToFile("old_preset_migrated.json");
}

Example 2: Check Compatibility

Version v1{1, 0, 0};
Version v2{2, 5, 0};

auto manager = VersionManager::getInstance();

if (manager.isCompatible(v1, v2)) {
    std::cout << "Can migrate from v" << v1.toString()
              << " to v" << v2.toString() << "\n";

    // Show migration path
    auto path = manager.getMigrationPath(v1, v2);

    std::cout << "Migration path: ";
    for (const auto& step : path) {
        std::cout << step.toString() << " โ†’ ";
    }
    std::cout << "done\n";
}

Example 3: Safe Migration with Rollback

auto manager = VersionManager::getInstance();
auto target = Version{2, 0, 0};

try {
    // Create backup
    auto backup = preset;

    // Attempt migration
    preset = manager.migrate(preset, target);

    // Validate
    if (!preset.validate()) {
        throw MigrationError("Validation failed");
    }

    std::cout << "โœ… Migration successful\n";

} catch (const MigrationError& e) {
    std::cerr << "โŒ Migration failed: " << e.what() << "\n";

    // Rollback
    preset = backup;
    std::cerr << "Rolled back to v" << preset.getVersion().toString() << "\n";
}

๐ŸŽฏ Performance Targets

Operation Target Typical
Version parsing <1 ฮผs 0.5 ฮผs
Compatibility check <10 ฮผs 5 ฮผs
Single migration <10 ms 5 ms
Chain migration (3 steps) <30 ms 15 ms
Rollback <5 ms 2 ms

๐Ÿงช Testing Strategy

Test Coverage

  1. Unit Tests
  2. Version parsing and comparison
  3. Migration script logic
  4. Compatibility checking

  5. Integration Tests

  6. Full migration chains
  7. Rollback scenarios
  8. Multi-version compatibility

  9. Regression Tests

  10. Real legacy presets
  11. Edge cases (very old versions)
  12. Performance tests

Test Scenarios

TEST_CASE("Version Parsing") {
    REQUIRE(Version::parse("1.2.3") == Version{1, 2, 3});
    REQUIRE(Version::parse("v1.0.0") == Version{1, 0, 0});
}

TEST_CASE("Migration Chain") {
    auto preset = createV1Preset();
    auto migrated = manager.migrate(preset, Version{2, 0, 0});

    REQUIRE(migrated.getVersion() == Version{2, 0, 0});
    REQUIRE(migrated.validate());
}

TEST_CASE("Rollback on Failure") {
    auto preset = createCorruptedPreset();

    REQUIRE_THROWS(manager.migrate(preset, Version{2, 0, 0}));

    // Should still have original version
    REQUIRE(preset.getVersion() == Version{1, 0, 0});
}

๐Ÿ“š API Reference

Version Class

class Version {
public:
    int major, minor, patch;

    std::string toString() const;
    static Version parse(const std::string& str);

    bool operator<(const Version& other) const;
    bool operator==(const Version& other) const;
    bool isCompatibleWith(const Version& other) const;
};

VersionManager Class

class VersionManager {
public:
    static VersionManager& getInstance();

    // Migration
    PresetSchema migrate(const PresetSchema& preset, const Version& target);
    bool canMigrate(const PresetSchema& preset, const Version& target);
    std::vector<Version> getMigrationPath(const Version& from, const Version& to);

    // Compatibility
    bool isCompatible(const Version& v1, const Version& v2);
    CompatibilityMatrix getCompatibilityMatrix() const;

    // Version info
    Version getCurrentVersion() const;
    std::vector<Version> getSupportedVersions() const;
    std::vector<BreakingChange> getBreakingChanges(const Version& from, const Version& to);
};

๐Ÿš€ Getting Started

1. Basic Usage

#include "version_manager.hpp"

// Load preset
auto preset = PresetSchema::loadFromFile("preset.json");

// Migrate to current version
auto manager = VersionManager::getInstance();
auto migrated = manager.migrate(preset, manager.getCurrentVersion());

// Save
migrated.saveToFile("preset_updated.json");

2. Add New Migration Script

// Create migration script
class Migration_1_5_to_1_6 : public MigrationScript {
    Version from_version() const override { return {1, 5, 0}; }
    Version to_version() const override { return {1, 6, 0}; }

    PresetSchema migrate(const PresetSchema& preset) const override {
        // Your migration logic here
    }
};

// Register migration
VersionManager::getInstance().registerMigration(
    std::make_unique<Migration_1_5_to_1_6>()
);

๐Ÿ“ Best Practices

For Developers

  1. Always increment version when changing schema
  2. Write migration scripts for breaking changes
  3. Test with real presets from all versions
  4. Document breaking changes clearly
  5. Provide rollback when possible

For Users

  1. Backup presets before major updates
  2. Test migrations on copy first
  3. Report migration issues immediately
  4. Keep old version available if needed

  • Preset Schemas (TAREA 1): Define structure to migrate
  • Serialization Engine (TAREA 2): Load/save different versions
  • Validation System (TAREA 5): Verify migrated presets
  • Cloud Integration (TAREA 10): Sync version-aware presets

๐Ÿ“Š Metrics & Monitoring

Key Metrics

  • Migration success rate: >99%
  • Rollback rate: <1%
  • Average migration time: <20ms
  • Compatibility coverage: 100% within MAJOR version

Monitoring

  • Track migration failures
  • Log breaking changes encountered
  • Monitor performance of migration chains
  • Alert on incompatible versions

Status: ๐Ÿ”„ In Development Last Updated: 2025-01-15 Version: 1.0.0