Skip to content

A Unity Save Migration Checklist That Preserves Player Progress

Plan a Unity save migration with versioned fixtures, explicit defaults, interruption tests, and recovery rules that preserve the original player data.

Published

4 min read

Changing a save format is a compatibility change even when the new code compiles. The old game may have written values that the new game interprets differently. A successful migration needs to preserve meaningful progress, reject unsupported data safely, and survive an interrupted write.

Imagine a puzzle game replacing one unlocked-level number with a set of stable level identifiers. The migration must translate the old meaning into the new model. Parsing the old file without an exception does not prove that the right levels remain available.

/01

Define supported versions and invariants

List the save versions the candidate must read and the version it will write. State what should happen with an unknown newer version, a truncated file, and a missing file. Keep those outcomes distinct: new-player defaults are appropriate for an absent save, but silently replacing an unreadable existing save can destroy recoverable progress.

Write the invariants in player terms. In the puzzle example, previously completed levels should remain completed, currency should not duplicate, and an unknown level identifier should have a documented treatment. Decide how a player can recover if migration cannot complete. Make that decision before implementation so the error handler cannot quietly invent a destructive fallback.

/02

Create fixtures from the old writing behavior

Use disposable data from the previous supported build to create fixtures for a fresh player, partial progress, completed content, and meaningful boundary values. Preserve the originals and expected outcomes separately. A fixture created only by the new serializer may miss the very incompatibility being investigated.

Include optional fields that the old build omitted and values near the allowed limits. Add deliberately invalid copies without altering the valid originals. Unity’s serialization documentation describes the data model its serializer supports; compare your actual save library’s rules with the version in use. Do not assume every JSON library or Unity serialization path handles fields identically.

/03

Separate conversion from reading and writing

Keep the transformation inspectable: old validated data enters, proposed new data leaves, and validation checks the result before persistence. Map the old unlocked-level position through an explicit historical level order. Using today’s order could unlock a different puzzle after designers rearrange the campaign.

Make defaults deliberate. A new accessibility setting can use a documented default, while a newly required ownership field may need a different treatment. Test each supported conversion path independently and ensure that loading already migrated data does not apply the conversion again. The migration should have a clear owner rather than being scattered through unrelated gameplay scripts.

/04

Protect the original at the storage boundary

Retain a recoverable original until the new representation has been written and verified under the storage system’s documented guarantees. Define how interrupted writes are detected and which copy wins after a restart. Implement this with platform-appropriate storage operations; do not assume a desktop file replacement technique has identical behavior in a browser build.

Unity’s persistentDataPath is a platform-dependent location, not a complete migration or backup protocol. Verify the target’s actual persistence behavior and keep tests confined to disposable save locations. If saving fails, report that failure accurately and preserve the prior durable state. An in-memory success message should not claim that the migration survived application exit.

/05

Reopen migrated saves in the candidate build

Run the candidate against each fixture, close it, then open the resulting save again. Check the invariants through gameplay as well as data assertions. In the example, enter an old completed level, complete a newly available one, and verify both states on the next launch.

Test the documented recovery path when conversion or writing fails. Also decide whether the previous application version can read the newly written format; if it cannot, deployment rollback must account for save compatibility. Record that limitation before release. The acceptance result should identify supported versions and verified recovery behavior, not merely report that one test save loaded.

Keep this in mind

Key takeaways

  • Treat save migration as a versioned compatibility contract with explicit player-state invariants.
  • Keep historical fixtures and recoverable originals separate from the candidate conversion output.
  • Verify interrupted writes, reopened saves, and rollback compatibility on the actual storage target.

Sources & further reading

Consult the source documentation for the version and platform you are working with.