Serialization best practices
Best practices for serialization.
Read time 6 minutesLast updated 13 days ago
Apply the following coding practices and organizational principles to prevent errors and optimize serialization performance in your project.
Thread safety
Most Unity APIs can only be called from the main thread. Don't call Unity APIs from constructors and field initializers of serializable types because these run on a separate loading thread.
In development builds, Unity throws an error if you attempt to call main thread APIs from a loading thread. In a release build, the code runs without errors but can produce crashes and other unexpected behavior in your application.
Finalizers
Don't initiate serialization from finalizer methods. Finalizers don't run on the main thread. The garbage collector determines when they run and in some circumstances they might not run at all.
Attempting to serialize data from a finalizer can lead to unexpected behavior and crashes. For more information, refer to the Microsoft documentation on Finalizers.
Organizing your data
You can organize your data as follows to ensure optimal use of Unity's serialization:
- Aim to have Unity serialize the smallest possible set of data. The purpose of this isn't to save space on your computer's hard drive, but to make sure that you can maintain backwards compatibility with previous versions of the project. Backwards compatibility can become more difficult later on in development if you work with large sets of serialized data.
- Never have Unity serialize duplicate data or cached data. This causes significant problems for backwards compatibility: it carries a high risk of error because data can get out of sync.
- Use caution when adding fields that reference custom C# classes or structs. When you reference custom C# classes or structs in fields, Unity embeds the whole content of the referenced object in the serialized data of the referencing object. If this structure becomes complex or deeply nested then it can be hard to migrate this data as you change your structure in the future. Also the data from an object can easily be repeated unintentionally more than once in the serialized data. Instead, aim to keep the structure within your MonoBehaviour and ScriptableObject derived classes flat and simple.
- The way to share data between Objects is to use ScriptableObjects. When you reference a class derived from (such as a class derived from ScriptableObject), Unity only serializes the reference and not the entire object graph. So the data is only serialized once, in the ScriptableObject, not at each point that references it. Similarly, you can use the
UnityEngine.Objectattribute if you want to reference the same custom C# class more than once within a single MonoBehaviour or ScriptableObject. For more information, refer to Serialization of custom classes.[SerializeReference]
Clean up stale serialized data
When you change the structure of a script or a prefab, Unity doesn't immediately rewrite every asset that already references the old structure. Existing serialized data stays on disk until the affected asset is saved again. As a result, assets can accumulate stale serialized data: fields and placeholder objects that no longer correspond to anything in the current project.
How stale data accumulates
Stale data commonly appears in , , and files in the following ways:
.unity.prefab.asset- Removed serialized fields: If you remove a field from a
[SerializeField]orMonoBehaviourscript, any asset that previously assigned a value to that field still contains the value in its YAML data. The value is invisible at runtime because the field no longer exists in the class, but it remains on disk until the asset is saved again.ScriptableObject - Stripped placeholders for nested prefab objects: When a scene or another prefab references an object inside a nested prefab instance, Unity adds a stripped placeholder object to the containing file. If the referenced object is later removed from the source prefab, the placeholder remains in the containing file until that file is saved again.
- Unused prefab overrides: When a script field referenced by a prefab override is renamed or deleted, the override entry on the prefab instance becomes unused. For information on how to remove unused overrides specifically, refer to Remove unused override data.
Why Unity doesn't automatically remove stale data
Unity intentionally leaves stale data in place rather than removing it on every script or prefab change for two reasons:
-
Performance: Removing it would require Unity to load, modify, and save every asset that depends on the changed script or prefab. For large projects this can mean rewriting thousands of files for a single edit.
-
Data recovery: Unity preserves stale data so you can reverse a temporary removal without losing values. For example:
- If you delete a serialized field and add it back with the same name, the previously serialized values reattach automatically.
- If you delete a referenced asset and restore it together with its file (so the GUID is unchanged), references to it remain valid.
.meta
To preserve data when you rename a field instead of removing it, use the attribute.
[FormerlySerializedAs]Reserialize affected assets
To remove stale data from a single asset, modify any property on the asset in the Editor and save it. This forces Unity to rewrite the asset using the current scripts, prefabs, and asset references, dropping any data that no longer corresponds to a field or referenced object.
To clean up stale data across many assets, call the method from an Editor script. The following example reserializes every asset in the project from a menu item:
AssetDatabase.ForceReserializeAssetsusing UnityEditor;public static class StaleDataCleanup{ [MenuItem("Tools/Reserialize All Assets")] static void ReserializeAll() { AssetDatabase.ForceReserializeAssets(); }}
Without arguments, reserializes every asset in the project. To reserialize a specific subset, pass a list of asset paths and an optional value to control whether the assets, their files, or both are rewritten.
AssetDatabase.ForceReserializeAssetsForceReserializeAssetsOptions.metaReserializing many assets at once produces a large set of file changes. Coordinate with your team and your version control system before running it across a project.