Introduction
A Unity project can start with one small JSON file and remain perfectly healthy for a long time.
As the project grows, however, the data requirements usually split in different directions:
- Some files must be easy for humans to edit.
- Some must produce readable Git diffs.
- Some must load quickly on a mobile device.
- Some must be shared with a server or an operations tool.
- Some must remain compatible with older clients or save files.
- Some must work correctly under IL2CPP and AOT restrictions.
Trying to satisfy all of those requirements with one format eventually creates conflicts.
This article compares YAML, JSON, and MessagePack from a production Unity perspective. It focuses on user settings, save data, runtime distribution, server communication, and static game data—often called master data in Japanese game development.
The central idea is simple:
Use human-friendly formats at authoring time, machine-friendly formats at runtime, and choose network formats by balancing observability against efficiency.
The question is not which format is universally best. The useful question is who reads the data, who edits it, when it is validated, and where it is consumed.
The Practical Answer First
The following table is a useful starting point for many Unity projects.
| Use case | Good default | Why |
|---|---|---|
| Hand-written configuration | YAML | Comments, hierarchy, and reviewable diffs |
| Simple external integration or Web API | JSON | Broad compatibility and excellent tooling |
| Small Unity user settings | PlayerPrefs or JSON | Minimal implementation cost and easy inspection |
| Save data | JSON or MessagePack | Choose between observability and size/parse cost |
| Authoring static game data | Spreadsheet, Excel, YAML, or CSV | Match the format to the people editing it |
| Runtime static game data | MessagePack, generated code, or a project-specific binary | Favor validated data and efficient loading |
| Initial server API implementation | JSON | Easier debugging, logging, and integration |
| High-volume or high-frequency APIs | JSON + gzip or MessagePack | Reduce network size, parse cost, or both |
| Operations tools, admin panels, and logs | JSON | Easy for both people and tools to consume |
The common mistake is to make one global rule:
- “YAML is readable, so everything should be YAML.”
- “JSON is standard, so everything should be JSON.”
- “MessagePack is fast, so everything should be MessagePack.”
A more useful mental model is:
- YAML leans toward human authoring.
- JSON leans toward interoperability and investigation.
- MessagePack leans toward runtime efficiency.
What to Evaluate Before Choosing a Format
Will people read or edit it directly?
For hand-edited data, readability and diff quality matter more than raw parsing speed.
YAML supports comments and can represent nested structures without as much punctuation as JSON. JSON is still reasonably readable, but standard JSON does not allow comments or trailing commas. MessagePack is not intended for direct editing at all.
How much data is there, and how often is it loaded?
For a few small settings, the difference between YAML and JSON is rarely important.
At tens of thousands of rows or millions of cells, the costs become visible:
- Parsing
- String allocation
- Numeric conversion
- Garbage collection
- File size
- Object construction after deserialization
The acceptable threshold depends on the target device, compression, load frequency, and the shape of the resulting object graph. Measure with representative data instead of relying on format reputation.
For large runtime datasets, possible options include:
- MessagePack
- A project-specific binary format
- Generated C# data or loaders
- SQLite
- FlatBuffers
- MemoryPack
- Partitioned ScriptableObject assets loaded through Addressables
MessagePack is a strong option, but it is not the only runtime-oriented format.
Is it network data or a local file?
Network data passes through more systems than a local file:
- The server
- The client
- Logging and monitoring
- Debug proxies
- Customer-support tooling
- Admin panels
- Automated tests
Binary formats can reduce payload and parsing costs, but they also make incident investigation harder.
It also helps to separate serialization format from compression.
For example:
- JSON can be sent with gzip to reduce transfer size.
- MessagePack can reduce representation size and parsing overhead.
- MessagePack can also be compressed, although that is a separate decision.
A practical distinction is:
- Use JSON when compatibility and observability dominate.
- Use JSON + gzip when bandwidth is the main issue and JSON parsing is still acceptable.
- Consider MessagePack when parsing time, allocation, or very frequent traffic is also a bottleneck.
How must schema changes be handled?
Games regularly add fields, rename data, change types, and support older clients or save files.
The important part is not only the format. It is the versioning policy.
JSON includes field names, so readers can often ignore unknown fields. MessagePack can behave similarly with map-style string keys, though this reduces some of its size and speed advantages. Array-style integer keys can be more compact, but key-number management becomes part of the compatibility contract.
YAML is often best treated as an authoring format. Validate and normalize it during conversion, then give the generated runtime data an explicit version.
YAML: Strong for Human-Edited Configuration
YAML is designed to be comfortable for people to read and write.
Typical uses include:
- Build configuration
- CI configuration
- Tool settings
- Environment settings
- Small data definitions
- Conversion rules
A stage definition might look like this:
stages:
- id: stage_001
name: Beginning Forest
enemyLevel: 3
bgm: forest_day
notes: "The first stage after the tutorial"
- id: stage_002
name: Old Mine
enemyLevel: 6
bgm: mine
notes: "Dark area with limited-visibility mechanics"
One of YAML's most practical advantages is comments:
# Early-game stage. Keep enemy attack values low.
- id: stage_001
enemyLevel: 3
# New players tend to struggle here, so give slightly more gold.
rewardGold: 120
In game development, comments such as “why this value exists” or “this is a temporary tuning override” can be more valuable than the syntax itself.
Where YAML fits in a Unity project
YAML is useful for:
- Configuration edited directly by engineers
- Data that needs comments
- Settings reviewed in Git
- Environment-specific configuration
- Build and CI tooling
- Small or medium hand-written datasets
- Source data converted before the player build
Examples include:
- Addressables helper configuration
- Custom build-pipeline settings
- Data-conversion rules
- Localization conversion rules
- Static-data generation settings
- Development cheat-menu definitions
- Server endpoint definitions by environment
This does not mean manually editing Unity's serialized .prefab, .unity, or .asset text files. The YAML discussed here is project-owned configuration or source data.
Unity does not provide a general-purpose YAML parser comparable to JsonUtility. A practical YAML workflow usually depends on one of the following:
- A library such as YamlDotNet
- A project-specific conversion tool
- A CI conversion step
- An external data pipeline
Reading YAML directly at runtime is possible, but it introduces parsing cost, allocations, package size, error handling, and IL2CPP/AOT considerations. In many projects, a safer workflow is to convert YAML into JSON, MessagePack, generated code, or another validated runtime format during the build or CI process.
YAML is also not automatically the best authoring format for every static dataset. When designers need to edit thousands of rows as a table, a spreadsheet, Excel workbook, or dedicated admin UI is usually more practical.
YAML pitfalls
YAML is convenient, but its flexibility can produce surprising behavior.
Consider these values:
value1: 001
value2: yes
value3: 2026-07-08
Whether they are interpreted as strings, numbers, booleans, or dates can depend on the YAML version and parser behavior.
For example, tools that retain YAML 1.1 behavior may interpret yes as a boolean. YAML 1.2 narrowed implicit typing, but production projects still need to verify the behavior of the selected parser and surrounding tools.
Game data often uses IDs and code-like values that must remain strings:
characterId: "001"
itemId: "yes"
releaseDate: "2026-07-08"
A safe project rule is to quote IDs, date-like values, and codes with leading zeroes.
YAML also supports anchors and aliases:
defaultEnemy: &defaultEnemy
hp: 100
attack: 20
defense: 5
enemies:
- <<: *defaultEnemy
id: slime
hp: 80
- <<: *defaultEnemy
id: goblin
attack: 25
This can remove duplication, but heavy use makes the final resolved value harder to see during review.
The << merge key in this example comes from a YAML 1.1-era extension and is not part of the YAML 1.2 core schema. Support and configuration vary by parser, so confirm the exact behavior before relying on it.
For production data, it is often wise to restrict the YAML features allowed by the project and emit a normalized representation during conversion.
JSON: The Default for Interoperability and Investigation
JSON is a practical general-purpose data-exchange default.
It is readable text, supported by almost every server language, and deeply integrated into HTTP tooling, browsers, command-line utilities, admin panels, logs, and monitoring systems.
Why JSON remains so useful
JSON is not the most compact or fastest format. Its strength is the surrounding ecosystem.
When an API fails, a JSON request or response can be inspected directly in:
- Server logs
- Browser developer tools
curl- Postman
jq- Monitoring dashboards
- Automated scripts
That makes JSON a very practical starting point for game-server APIs.
JSON limitations
JSON has several weaknesses:
- Standard JSON has no comments.
- Binary data must be encoded separately, often with Base64.
- Payloads can become large because field names repeat.
- Parsing requires text processing.
- Numeric interoperability needs care.
The numeric issue is especially important for 64-bit IDs. Unity and C# can represent a value as long, while JavaScript or another consumer may lose integer precision. Large account IDs, transaction IDs, or order numbers are often safer as strings across system boundaries.
In Unity, loading large JSON documents at runtime also deserves measurement. Parsing several megabytes or tens of megabytes at startup and materializing the entire object graph can affect startup time, peak memory, and garbage collection.
That does not make JSON a bad format. A few kilobytes of settings, a small save file, or a low-frequency API response may be completely fine. Problems appear when the data size and access pattern outgrow the original design.
Unity's JsonUtility
Unity includes JsonUtility.
It is lightweight for simple types that follow Unity's serialization rules. In suitable cases, it can allocate less than a general-purpose JSON library.
However, JsonUtility is not a fully general JSON document API. It works with structured JSON that maps to known C# types and inherits important Unity-serialization limitations.
Notable constraints include:
-
Dictionary<TKey, TValue>is not supported. - Top-level arrays and primitive values are awkward and usually need wrapper classes.
- Arbitrary JSON trees are not its intended use case.
- Fine-grained naming and polymorphism support are limited.
It works well for:
- Simple configuration classes
- Small save DTOs
- Inspector-friendly data shapes
- Known request or response structures
For complex server responses, dictionaries, custom naming rules, or polymorphic data, Newtonsoft.Json may be a better fit.
System.Text.Json can also be considered, but first verify the exact Unity version, .NET profile, IL2CPP/AOT behavior, source-generation setup, package integration, and stripping behavior. Success in the Editor under Mono does not prove that an iOS or Android IL2CPP build is production-ready.
ToJson still creates a JSON string, and FromJson creates a new object. Large datasets still allocate and must be profiled. When updating an existing instance is appropriate, FromJsonOverwrite can reduce object replacement and is worth considering.
MessagePack: A Runtime-Oriented Binary Format
MessagePack represents JSON-like structures in binary form.
Its main appeal is that it can be smaller than JSON and faster to serialize or deserialize, especially for numeric, array-heavy, or frequently processed data.
In Unity, it becomes attractive when one of these costs is visible in profiling:
- JSON parsing
- Temporary string allocation
- Garbage collection
- Startup loading
- Network transfer
- Large local caches
Where MessagePack fits well
MessagePack is most useful when machines, rather than people, own the final data.
A common static-data pipeline is:
Spreadsheet / YAML / CSV
↓ conversion and validation
MessagePack / project-specific binary
↓
Unity runtime
Designers and engineers work with a readable source format. The game loads a validated runtime artifact.
That conversion step separates authoring convenience from runtime efficiency.
MessagePack performance is conditional
“MessagePack is fast” is a useful tendency, not a universal guarantee.
Performance, size, and allocation depend on:
- The selected library
- Resolver configuration
- String keys versus integer keys
- DTO shape
- Generated serialization code
- IL2CPP/AOT setup
- Compression settings
- The number of objects created after deserialization
MessagePack is also not compression. If LZ4 or another compressor is added, measure both compression ratio and decompression cost with representative data.
Even fast deserialization does not help much if the application then:
- Builds an enormous object graph
- Runs LINQ queries repeatedly over the data
- Loads content that is not needed at startup
- Copies results into multiple collections
- Uses inefficient lookup structures
If JSON is transmitted with HTTP gzip compression, its transfer size may approach MessagePack for some payloads. The JSON still has to be decompressed, parsed, and converted into objects, so profile network cost separately from CPU time and garbage collection.
For some datasets, generated C# code, SQLite, FlatBuffers, MemoryPack, a custom binary, or partitioned ScriptableObjects may be more appropriate. MessagePack should earn its place through measurements and operational fit.
MessagePack operational and security considerations
The biggest operational weakness is that people cannot inspect MessagePack directly.
It is not suitable for hand-editing or Git diff review. During an incident, opening a binary file does not immediately explain what it contains.
A production-friendly MessagePack workflow should include:
- A debug exporter that produces JSON
- A decode CLI or internal inspection tool
- Explicit data versions
- Schema validation
- Conversion logs
- Source-data references
- Clear load-error messages
- Checksums or hashes for corruption detection and artifact matching
Deserializing external input also makes the deserializer configuration part of the attack surface.
With MessagePack for C#, consider the following for untrusted input:
- Use
MessagePackSecurity.UntrustedDatawhere appropriate. - Avoid Typeless APIs and resolvers unless the use case is tightly controlled.
- Deserialize only approved DTO types.
- Apply input-size limits.
- Track security advisories and patch versions.
- Validate the resulting DTO after deserialization.
A safer deserializer mode is not a substitute for validation or server-side trust boundaries.
Unity IL2CPP builds add AOT constraints. As of July 2026, MessagePack for C# v3 uses source generation for AOT workflows and lists Unity 2022.3.12f1 as its minimum supported version. Older Unity releases or older package versions require their own compatibility checks.
Even with current versions, test custom resolvers, generic types, code stripping, error paths, and actual device builds early. Package requirements can change, so confirm the official documentation for the exact Unity and package versions used by the project.
MessagePack is not encryption
MessagePack is harder to read in a text editor, but it is not encrypted. Anyone who understands the format can decode it.
Do not treat “binary” as tamper protection.
Checksums and ordinary hashes are useful for detecting accidental corruption or confirming that generated artifacts match. They do not stop an attacker who can modify both the data and the stored hash.
For malicious-tampering detection, consider:
- A keyed MAC
- A digital signature
- Server-side validation
- A server-authoritative design for important state
Encryption primarily provides confidentiality. It does not automatically provide complete tamper protection.
The same rule applies to network traffic. MessagePack does not replace HTTPS, authentication, authorization, replay defenses, or input validation.
Choosing Formats by Unity Use Case
User settings
Audio volume, graphics quality, vibration, language, and control preferences are normally small.
A reasonable default is:
- Use PlayerPrefs for a few independent values.
- Use JSON when the settings have a clear structure.
- Prefer JSON when easy inspection is valuable.
MessagePack is rarely necessary for ordinary user preferences.
PlayerPrefs should remain within its natural boundary. It is not a good place for a large structured save file, secret values, or data that requires tamper resistance.
Save timing also matters. Unity normally writes PlayerPrefs during application shutdown, but mobile platforms may terminate an app without a reliable quit callback. Important settings should therefore be saved at meaningful checkpoints controlled by the application, such as confirming an options screen or entering a pause state when appropriate.
Do not call PlayerPrefs.Save() for every slider movement. The write may cause a noticeable hitch. Save at deliberate points rather than continuously during gameplay.
Save data
Save-data decisions depend heavily on game size and write frequency.
For a small game, JSON is a reasonable starting point because:
- Developers can inspect it.
- Customer-support investigations are easier.
- Early schema changes are easier to understand.
For a large save file or frequent autosaves, MessagePack or a project-specific binary may be worth evaluating.
The format is only one part of save reliability. A robust save design also needs:
- A version number
- Migration from older versions
- Validation and normalization
- Writing to a temporary file first
- Controlled replacement of the previous file
- Backup and recovery behavior
- Corruption detection
- A policy for tampered data
JSON stored as plain text can be edited easily. Even in an offline game, edited values can cause cheating, support confusion, or crashes caused by impossible state.
Important state may require a keyed MAC, signature, or server validation. An ordinary hash is insufficient if the attacker can replace both the file and the hash.
Whether the payload is JSON or MessagePack, add a version field or equivalent from the beginning. Binary serialization does not create compatibility automatically.
Static game data
Static game data includes definitions such as:
- Characters
- Items
- Skills
- Stages
- Quests
- Shops
- Gacha tables
- Localization entries
This is one of the easiest areas to outgrow a single-format design.
A practical pipeline separates authoring from runtime delivery:
Authoring: Spreadsheet / Excel / YAML / CSV / dedicated admin UI
Conversion: Validate / normalize / generate code / export binary
Runtime: MessagePack / project-specific binary / generated C# data
Choose the authoring source based on the people and workflow:
- Designers editing large tables: Spreadsheet or Excel
- Engineers maintaining structured configuration: YAML
- Simple interoperable tables: CSV
- Live operations teams: Dedicated browser-based tooling
Choose the runtime format based on loading cost, memory behavior, validation guarantees, and update strategy.
Small datasets may remain in JSON without issue. If growth is expected, building the conversion and validation pipeline early is often more valuable than prematurely optimizing the final serializer.
A “project-specific binary” should mean a documented, validated runtime artifact with versioning, migration rules, debug export, and generation tooling. It should not merely mean “a file that is difficult to read.”
ScriptableObject and external formats
ScriptableObject is excellent for Unity-owned data that benefits from Inspector editing and asset references.
Examples include:
- Enemy AI tuning
- Effect references
- Prefab references
- Camera settings
- Small gameplay configuration assets
It is less convenient as the only solution for huge tabular datasets, data shared with a server, or data that needs spreadsheet-style editing and cross-table validation.
A useful division of responsibility is:
- ScriptableObject for Unity asset-oriented configuration
- Spreadsheet, YAML, or CSV for authoring external static data
- MessagePack or another binary for runtime distribution
Localization data
Localization datasets grow quickly and are often exchanged with translators.
Spreadsheet or CSV is usually convenient for authoring and vendor exchange. At runtime, partition by language or feature and convert the source into a structure that supports efficient ID lookup.
Addressables and AssetBundle metadata
Human-reviewed Addressables labels, download groups, or build settings may fit YAML. Generated metadata and tool-to-tool interchange often fit JSON.
There is usually no need to parse YAML on every player startup. Data known at build time should be validated and converted into a stable runtime representation.
Choosing a Format for Server APIs
Server communication is where the JSON-versus-MessagePack discussion becomes most visible.
The decision should include incident response, customer support, monitoring, and deployment—not only payload size.
APIs that usually fit JSON
JSON is a good default for APIs such as:
- Login
- User-profile retrieval
- News and announcement retrieval
- Shop information
- Static-data version checks
- Admin-panel APIs
- External-service integration
- Diagnostic APIs
- Low-frequency operations
For these endpoints, observability and compatibility often matter more than a small payload reduction.
JSON works naturally with curl, Postman, server logs, dashboards, and proxy tools.
APIs that may benefit from MessagePack
MessagePack becomes more attractive for:
- Very frequently called endpoints
- Large list responses
- Real-time or near-real-time traffic
- Battle logs or state synchronization
- Mobile traffic where transfer volume matters
- Endpoints where JSON parsing is a measured bottleneck
Adopting MessagePack should come with operational support:
- A JSON decoder for investigations
- API and data versioning
- Shared DTO rules between client and server
- Backward-compatibility policy
- Readable server-side diagnostic logging
- Request and response validation
Using MessagePack for every endpoint often adds more operational cost than it saves. Limiting it to measured hot paths is usually easier to maintain.
Using JSON in development and MessagePack in production
A development-versus-production switch can be useful, but it has a dangerous failure mode: testing only JSON in development while production alone uses MessagePack.
Avoid that design.
Instead, separate the wire format from the debug representation.
A practical setup is:
Local development: Switch between JSON and MessagePack
CI and staging: Test both, and always exercise the production path
Production: Use MessagePack only for endpoints where it provides value
Investigation: Render decoded DTOs as masked JSON
Keep the following common across both formats:
- DTO meaning
- Data version
- Normalization
- Validation
- Error handling
Change only the encoder and decoder based on Content-Type, Accept, or explicit configuration.
Automated tests can serialize the same DTO through JSON and MessagePack, deserialize both, and compare the normalized results. Once MessagePack is selected for production, engineers should exercise that path continuously rather than only before release.
This is especially important for IL2CPP/AOT behavior, resolver configuration, integer-key compatibility, compression, and client/server implementation differences.
When JSON + gzip remains the right production choice
Production does not automatically mean MessagePack.
If transfer size is the main problem while JSON parsing and allocation remain acceptable, JSON + gzip is a strong tradeoff.
Repeated property names and recurring text usually compress well. The project keeps the compatibility and investigation benefits of JSON while reducing bandwidth.
In HTTP terms, the media type and compression are separate concerns:
Content-Type: application/json
Content-Encoding: gzip
Small responses may not benefit because headers and compression work become significant relative to the payload. Use representative data to choose a minimum size threshold rather than compressing every response blindly.
If request bodies are also compressed, client and server support must be explicitly aligned.
gzip is compression, not encryption. It may prevent a raw byte dump from being immediately readable as plain JSON, but identifying and decompressing it is straightforward. Treat that as incidental obfuscation at most, not as a security boundary.
Use HTTPS/TLS for transport confidentiality and integrity. Use authentication, authorization, replay defenses where needed, and server-side validation for game rules.
A useful decision split is:
- Only transfer size is a problem: evaluate JSON + gzip.
- JSON parsing and GC are also a problem: evaluate MessagePack.
- Neither is a measured problem: keep plain JSON.
- The payload is a large binary asset: avoid Base64 inside JSON and use file delivery or a separate endpoint.
Mixing JSON and MessagePack is acceptable
A project does not need one wire format for every API.
For example:
Normal APIs and admin tools: JSON
Large or latency-sensitive endpoints: MessagePack
Logs and audit events: JSON Lines
Even when the encoding differs, preserve common DTO semantics, versions, validation rules, and monitoring conventions.
DTO Design Matters More Than the Serializer
A poor DTO remains difficult to maintain in every format.
Separate Unity runtime models from DTOs
Do not serialize a Unity gameplay object directly just because it already contains the values you need.
public sealed class Player
{
public string Name;
public int Level;
public GameObject AvatarPrefab;
public RuntimeAnimatorController Animator;
}
This class mixes data with Unity asset references and runtime state.
Create a dedicated DTO instead:
[Serializable]
public sealed class PlayerDto
{
public string name = "";
public int level;
public string avatarId = "";
}
The lowercase field names above intentionally produce name, level, and avatarId with JsonUtility. If the project uses PascalCase C# members, configure property naming through the selected JSON library instead.
DTOs should primarily contain values such as:
-
int,long,float, andbool - Strings
- Arrays and
List<T> - IDs that reference assets or definitions
Avoid putting these directly into persistence or network DTOs:
GameObject-
TextureorSprite -
ScriptableObjectreferences - Events
- Runtime caches
- UI state
Define field and null policies
With JSON and string-key MessagePack, field names become part of the serialized contract. With integer-key MessagePack, the key numbers become the contract.
Treat published fields like API fields:
- Do not casually rename them.
- Avoid removing them without migration.
- Prefer adding new optional fields.
- Normalize older data during loading.
Also define whether missing collections become null or empty collections.
A normalization step reduces repeated null checks in gameplay code:
public sealed class InventoryDto
{
public List<string> itemIds = new List<string>();
public void Normalize()
{
itemIds ??= new List<string>();
}
}
Validation should still reject impossible ranges, unknown enum values, oversized strings, and invalid IDs.
Manage MessagePack integer keys deliberately
With MessagePack for C#, integer keys may improve compactness and speed:
[MessagePackObject]
public sealed class CharacterDto
{
[Key(0)] public int Id { get; set; }
[Key(1)] public string Name { get; set; } = "";
[Key(2)] public int Hp { get; set; }
}
They also create a compatibility obligation.
A safe policy is:
- Never change an existing key number.
- Never reuse a deleted key number.
- Append new fields at the end.
- Give the data an explicit version where migration is required.
- Test old payloads against new readers.
String keys favor readability and evolution. Integer keys favor compactness and throughput. Choose based on data lifetime and compatibility needs, not only benchmark numbers.
Common Failure Modes
Failure 1: Using the authoring format directly at runtime
Reading designer-owned YAML or JSON directly in the player is convenient at first.
As the dataset grows, loading, allocation, validation, and error reporting become harder. Convert the source into a normalized runtime representation, remove comments and unused columns, validate references, and emit only the values the game needs.
Failure 2: Assuming JSON is safe because it is standard
External JSON and user-editable JSON are untrusted input.
Prepare for:
- Missing required fields
- Wrong types
- Out-of-range numbers
- Huge strings or arrays
- Unknown enum values
- Invalid IDs
JSON and MessagePack both require validation after deserialization.
Failure 3: Expecting MessagePack to make the whole load fast
MessagePack can reduce representation size and parse cost. It cannot fix inefficient data partitioning, repeated searches, unnecessary copies, or excessive object construction.
Measure the complete loading path.
Failure 4: Making binary data impossible to investigate
Binary formats reduce observability unless the team deliberately restores it.
Record enough metadata to answer:
- Which source produced this file?
- Which converter version was used?
- When was it generated?
- Which data version does it contain?
- How many records were loaded?
- Can it be exported back to readable JSON?
Without those answers, a compact file can become an expensive operational problem.
Recommended Project Structures
Small project
Keep the system simple:
User settings: PlayerPrefs / JSON
Save data: JSON
Static game data: JSON / ScriptableObject
Server APIs: JSON
Tool configuration: YAML
For small datasets, MessagePack's operational cost may exceed its benefit.
Medium project
Separate source data from runtime data:
User settings: JSON
Save data: JSON, with MessagePack evaluated if needed
Static-data source: Spreadsheet / YAML / CSV
Static-data runtime: MessagePack / project-specific binary
Server APIs: JSON
Large API payloads or production hot paths: JSON + gzip / MessagePack
Logs: JSON Lines
Run validation before the build and reject duplicate IDs, missing references, invalid ranges, and unsupported values.
Large or long-running project
Design the pipeline rather than choosing one serializer:
Authoring: Spreadsheet / Excel / YAML / dedicated admin UI
Conversion: Validate / normalize / generate code / export binary
Unity runtime: MessagePack / project-specific binary / generated loader
Server: JSON APIs / gzip compression / MessagePack hot paths
Operations: JSON logs / decode tools / data-version tracking
Make ownership explicit: where conversion happens, where validation happens, and how incidents are investigated.
A Simple Decision Flow
1. Do people edit the data directly?
Yes → YAML / Spreadsheet / CSV / dedicated UI
No → Continue
2. Must it integrate with external services or admin tools?
Yes → Start with JSON
No → Continue
3. Is network size or frequency a measured problem?
Yes → Evaluate JSON + gzip, MessagePack, or API partitioning
No → Continue
4. Does the Unity runtime load a large local dataset?
Yes → Evaluate MessagePack, generated code, partitioning, or another binary
No → JSON or ScriptableObject may be enough
5. Must people inspect the data during an incident?
Yes → Keep JSON in the workflow or provide a decoder
No → A binary-oriented representation may be acceptable
Do not try to predict one perfect format at the beginning of the project. Change formats at clear boundaries and optimize only the parts that need it.
Conclusion
YAML, JSON, and MessagePack solve different problems.
- YAML is strong for hand-written configuration, comments, and reviewable source data.
- JSON is strong for interoperability, observability, APIs, tools, and small datasets.
- MessagePack is strong for validated runtime data and measured hot paths where size, parsing, or allocation matters.
- JSON + gzip is a legitimate production option when bandwidth is the main issue and JSON parsing remains acceptable.
None of them is universal.
If MessagePack is introduced, introduce the supporting tools as well: decoders, versioning, validation, device testing, and representative benchmarks.
The durable rule is still this:
Use human-friendly formats at authoring time, machine-friendly formats at runtime, and choose network formats by balancing observability against efficiency.
References
- RFC 8259 — The JavaScript Object Notation Data Interchange Format
- YAML 1.2.2 Specification
- YamlDotNet
- MessagePack
- MessagePack for C#
- RFC 9110 — HTTP Semantics
- RFC 8446 — TLS 1.3
- Unity 6.4 — JsonUtility.ToJson
- Unity 6.4 — JsonUtility.FromJson
- Unity 6.4 — JsonUtility.FromJsonOverwrite
- Unity 6.4 — PlayerPrefs
- Unity 6.4 — PlayerPrefs.Save
- Unity 6.4 — MonoBehaviour.OnApplicationQuit
Top comments (0)