Skip to content

Versioning Strategy

Shadowfen edited this page Jul 29, 2026 · 1 revision

A well-designed versioning strategy is essential for maintaining saved data compatibility as an addon evolves.

LibSavedVars uses version numbers and migrations together to safely transform existing user data into the current format. A good versioning strategy ensures that users can upgrade from any previous addon release without losing their settings.


Version Numbers Represent Data Format

The saved variable version should represent the structure of the saved data, not the addon release number.

For example:

Addon Version: 2.5.0
Saved Data Version: 4

The addon may have many releases that do not change saved data.

Increase the saved variable version only when the stored data format changes.


When to Increase the Version

Increase the version when existing saved data requires transformation.

Examples:

Renaming a Setting

Before:

{
    showNames = true
}

After:

{
    displayNames = true
}

Requires migration:

settings:Migrate(2, function(savedVars)

    savedVars.displayNames = savedVars.showNames
    savedVars.showNames = nil

end)

Moving a Setting

Before:

{
    scale = 1.2
}

After:

{
    ui = {
        scale = 1.2
    }
}

Requires a migration because the location changed.


Changing Data Types

Before:

{
    color = "red"
}

After:

{
    color = {
        r = 1,
        g = 0,
        b = 0,
    }
}

The old value must be converted.


Changing Storage Location

Before:

Account-wide
└── build = "Tank"

After:

Character
└── build = "Tank"

Requires migration because the storage model changed.


When Not to Increase the Version

Many addon changes do not require a new saved data version.


Adding New Defaults

Example:

Version 1:

local defaults = {
    enabled = true,
}

Version 2:

local defaults = {
    enabled = true,
    showTooltips = true,
}

No migration is needed.

LibSavedVars automatically supplies the default value:

settings.showTooltips
-- true

Adding New Code Features

A new feature that does not modify existing saved data does not require a version change.

Examples:

  • New commands.
  • New UI panels.
  • New calculations.
  • New event handlers.

Version Numbering Recommendations

Use simple incremental numbers:

1
2
3
4
5

The saved data version is not intended to represent:

  • Major addon releases.
  • Minor releases.
  • Patch numbers.

Avoid:

1.2.3

or:

250

based on addon versions.

A data version answers:

"What format is this saved data?"


Migration Chain Design

Each version should have a migration that converts the previous format into the new format.

Example:

settings:Migrate(2, function(savedVars)

    -- Convert version 1 data to version 2

end)

settings:Migrate(3, function(savedVars)

    -- Convert version 2 data to version 3

end)

settings:Migrate(4, function(savedVars)

    -- Convert version 3 data to version 4

end)

Upgrade path:

Version 1
    |
    v
Migration 2
    |
    v
Migration 3
    |
    v
Migration 4
    |
    v
Current Version

Never Rewrite Released Migrations

Once a migration has been released, treat it as permanent.

Bad:

Version 2 migration
    changed after release

Good:

Version 2 migration
    unchanged forever

Version 3 migration
    added later

Users may upgrade directly from very old versions.

Removing or changing old migrations can break their saved data.


Keep Migrations Small

Each migration should perform one logical change.

Good:

settings:Migrate(3, function(savedVars)

    savedVars.newName = savedVars.oldName
    savedVars.oldName = nil

end)

Avoid:

settings:Migrate(3, function(savedVars)

    -- rename settings
    -- move tables
    -- convert formats
    -- remove old data
    -- rebuild profiles

end)

Large migrations are harder to test and debug.


Migration Ordering

Migrations execute in ascending version order.

Example:

settings:Migrate(2, migration2)
settings:Migrate(5, migration5)
settings:Migrate(4, migration4)

Execution order:

Migration 2
Migration 4
Migration 5

The order migrations are registered does not determine execution order.


Designing for Future Changes

When creating a new saved variable structure:

Use Clear Grouping

Prefer:

{
    ui = {
        scale = 1.0,
        theme = "Dark",
    },

    combat = {
        mode = "Normal",
    },
}

over:

{
    uiScale = 1.0,
    uiTheme = "Dark",
    combatMode = "Normal",
}

Grouped structures are easier to extend.


Avoid Unnecessary Nesting

Do not create deeply nested structures unless they provide value.

Hard to maintain:

settings.options.display.interface.window.position.x

Easier:

settings.window.x

Reserve Room for Growth

Design tables that can expand naturally.

Example:

profiles = {
    Default = {},
    Tank = {},
    Healer = {},
}

is easier to extend than separate unrelated tables.


Testing Version Upgrades

Every migration should be tested with old data.

Recommended tests:

Fresh Install

No existing data:

Version 0 → Current Version

Expected:

  • Defaults load correctly.
  • No migration errors.

Single Upgrade

Example:

Version 3 → Version 4

Expected:

  • Only migration 4 runs.

Multiple Upgrade

Example:

Version 1 → Version 5

Expected:

Migration 2
Migration 3
Migration 4
Migration 5

Customized Settings

Users rarely have default values only.

Test:

{
    scale = 2.0,
    theme = "Custom",
}

Ensure customizations survive migration.


Handling Failed Migrations

Migrations should fail safely.

Avoid:

savedVars.newValue =
    savedVars.oldTable.value

if oldTable may not exist.

Prefer:

if savedVars.oldTable then

    savedVars.newValue =
        savedVars.oldTable.value

end

Users may have incomplete or unusual saved data due to:

  • Previous bugs.
  • Manual editing.
  • Interrupted updates.
  • Removed addons.

Recommended Workflow

When changing saved data:

  1. Identify whether existing data changes.
  2. Increase the saved data version if needed.
  3. Add a migration for the change.
  4. Keep the migration permanent.
  5. Test upgrades from older versions.
  6. Release the update.

Summary

A good LibSavedVars versioning strategy:

  • Uses versions to track data formats.
  • Separates addon versions from data versions.
  • Increases versions only when data changes.
  • Uses migrations for transformations.
  • Keeps migrations permanent.
  • Tests upgrades from old data.
  • Designs saved structures for future growth.

A carefully planned version strategy allows an addon to evolve for years while preserving user configuration.

Clone this wiki locally