Skip to content
E
Egmatic
save systemsave game progressserializationgame statesave file

How to Save Game Progress: A Save System Guide for Developers

A save system does one job — serialize the player's game state, write it to storage, and read it back — but the hard parts are deciding what to save, avoiding corruption, and keeping saves working after you update the game. This guide covers what belongs in a save, the serialization formats to choose from (JSON, binary, protobuf), the atomic write pattern that prevents corrupt files, and how to version your save format so updates do not break old saves. Written for small studios and solo developers, grounded in how storage and serialization actually behave, with no invented statistics.

Vladislav KovnerovJuly 10, 20268 min

A save system has one job: take the player's game state, turn it into data, write that data to storage, and read it back later to restore the game exactly as it was. The reading and writing are simple. The hard parts are three: deciding what counts as state worth saving, writing in a way that can never corrupt the file, and keeping old saves working after you change the game.

This guide is the practical version for solo developers and small studios. It covers what belongs in a save, the serialization formats to choose from, the one pattern that prevents corruption, and how to version your data so an update does not break every save your players have made. Everything here reflects how storage and serialization actually behave — no invented retention numbers.

Save state, not derived data

The first decision is what to save. The rule is simple: save the data that defines the player's progress, and recompute everything else.

Save the things the player earned or chose — character position, inventory, current level, completed objectives, unlocked abilities, story choices, and settings like audio levels and control mappings. Do not save anything you can derive: cached enemy positions, precomputed totals, transient visual state.

Save itRecompute it
Player position, health, inventoryCamera position, current animation frame
Level completed flags, unlocked contentPathfinding caches, derived score totals
Story choices, settingsParticle positions, temporary effects

The reason to leave derived data out is not just size. If you store a value that can be recalculated, and your game logic changes, the stored value becomes a contradiction. A save that holds both the source data and a stale derived total will disagree with itself. Save the source, recompute the rest, and the save is always internally consistent.

Choose a serialization format

Serialization is the act of turning your in-memory objects into bytes you can store, and back again. Three formats cover almost every game.

FormatReadableSizeSpeedBest for
JSONYes (text)LargerModerateDevelopment and most small games
BinaryNoSmallFastLarge saves, performance-sensitive loads
Protocol Buffers (protobuf)No (but schema)SmallFastStrict schemas, cross-language

The honest recommendation for a small game is JSON. A save file you can open in a text editor and read is worth far more than the bytes a binary format saves you — when a save breaks in the wild, you will want to read it. Reach for binary or protobuf only when your saves are large enough, or your loads slow enough, that the size actually matters. For most 2D games it never does.

One note on the readability trade-off: a human-readable save file is also a file a curious or malicious player can edit. That is fine for a single-player game, and arguably a feature for the modding community. Treat it as a consideration, not a blocker.

The atomic write pattern

This is the single most important technique in the whole guide, and the one most indie games get wrong.

The naive approach is to open the save file and write to it directly. The problem is that writing is not instantaneous, and if the game crashes, the device loses power, or the OS interrupts the process mid-write, you are left with a file that is half old data and half new — a corrupt save the game cannot read.

The fix is to never write to the live file. Instead:

  1. Write the new save data to a temporary file.
  2. Rename the temporary file over the real one.

Renaming a file is an atomic operation on the same volume — it either completes in full or does not happen at all. A crash during the write leaves the temporary file partial, but the real save file is untouched and still valid. A crash during the rename is safe because the OS guarantees the rename itself is atomic. Either the player keeps their old save, or they get the new one — never a broken half.

Failure momentResult
Crash while writing temp fileOld save intact, temp file is garbage (discard it)
Crash during renameOne of the two files is whole, never a blend
Clean completionNew save replaces old

This pattern costs almost nothing to implement and removes essentially all save corruption from power loss and crashes. Combined with keeping one or two backup slots, it makes lost saves a rare event instead of a recurring support ticket.

Version your save format

The day you ship is the day your save format becomes a contract. Players will have saves sitting on their devices across every version of your game you ever release, and each one has to load.

Store a version number in every save file, and never assume the shape of the data on load:

  • On load, read the version first.
  • Migrate older formats to the current one, step by step — version 1 to 2, then 2 to 3.
  • Give every missing field a sensible default.
  • Map renamed fields to their new names.

The discipline is to change the format only through a migration. Add a field, bump the version, write the migration that fills the default for older saves. Never silently change what an existing field means, because a player's old save will load the old meaning under the new code and break.

ChangeSafe migration
Add a new fieldDefault value for older saves
Remove a fieldStop reading it; ignore in old saves
Rename a fieldMap old name to new in the loader
Change a value's meaningNew version number, explicit conversion

A versioned, migrated save format is what separates a game whose updates keep old saves working from one whose updates wipe player progress. Players do not forgive the second.

When and where to save

Two independent questions: where the data lives, and how often you write it.

Where is partly a platform decision. Local files are the baseline. Platform cloud systems (the console networks, platform-specific sync) replicate saves across devices, which players expect but which adds conflict rules — when a cloud save and a local save disagree, you need a policy, usually "newest wins." Design for the conflict before you hit it.

How often is a design choice with technical consequences:

  • Checkpoint saving writes only at defined points (end of level, save room). Simple, reliable, and a deliberate part of the difficulty.
  • Autosave writes periodically or on meaningful events. Convenient, but it must never block the game or cause a hitch — save on a background thread or between frames.
  • Save anywhere gives the player full control but exposes every edge case in your state serialization, because the player can save at any instant.

The technical rule that covers all three: never save on the hot path in a way the player can feel. Serialization and disk writes take time; do them where the frame budget has room.

Common mistakes

  • Saving derived data. Store source state, recompute the rest, or your save will contradict itself after an update.
  • Writing directly to the live save file. A crash mid-write corrupts it. Write to a temp file and rename.
  • No version number. The first update breaks every existing save.
  • Blocking the main thread. A save that hitches the game is felt every time it runs.
  • One slot, no backup. A single corrupted save loses everything. Keep a backup slot.
  • Assuming cloud saves solve conflicts. They introduce conflicts; define a resolution policy.
  • Saving everywhere as a substitute for design. "Save anywhere" is convenient for the player but the hardest to serialize correctly — choose it on purpose, not by default.

Where Egmatic fits

Egmatic is built around versioned JSON data. The editor produces game data as structured JSON that the engine consumes, so serialization is already a first-class concern in the architecture rather than something bolted on at the end. That same discipline carries straight into saves: a clean, versioned, human-readable format you can inspect when something goes wrong and migrate when your game changes. Egmatic's live preview lets you save, reload, and confirm the state restores exactly — turning the most boring part of save development, testing that saves round-trip, into a fast cycle instead of a slow rebuild loop.

For the serialization habits this article assumes, our guide to MonoGame explains the C# foundation Egmatic is built on, and the 2D performance guide covers the discipline of keeping work off the hot path — the same rule that keeps saving from hitching the game.

Conclusion

A save system is not a feature you add in the last week. It is the discipline of saving source state rather than derived data, choosing a format you can debug, writing atomically so a crash can never corrupt a file, and versioning the format so updates never break a player's progress. Get those four things right and your saves stop being a support burden and start being invisible — which is exactly what they should be.