Schema Versioning and Management
Last Updated: September 10, 2025
This document outlines the process for modifying and versioning JSON schemas in this repository. Following these guidelines is crucial for maintaining stability, ensuring backward compatibility, and allowing our systems to evolve safely.
Our strategy is built on Global Versioning and the principle of Immutability for released versions.
Quick Start
Installation
# Install schema tooling
npm install
# Generate all platform types
npm run codegen
Available Commands
| Command | Description |
|---|---|
npm run codegen | Generate types for all platforms |
npm run codegen:ts | Generate TypeScript types only |
npm run codegen:swift | Generate Swift models only |
npm run codegen:python | Generate Python models only |
npm run test:schemas | Full CI validation suite |
Developer Workflow
Core Philosophy
- Released Versions are Immutable: Once a version (e.g.,
v1) is in production use, its schemas must not be changed in a backward-incompatible way. - Versioning is Global: A version number (
v1,v2, etc.) applies to the entire system as a cohesive unit. A breaking change in any schema contributes to the next global version. - We Use a Fallback System: Our application code is designed to resolve schemas by first looking for the requested version (e.g.,
v2) and falling back to the previous version (v1) if it's not found. This allows us to only copy files that have actually changed.
Folder Structure
Our schemas are organized by their domain and then by version:
src/
├── layers/
│ └── v1/
│ └── image.json
│ └── v2/
│ └── image.json
└── story/
└── v1/
└── timeline.json
How to Make Changes
Your first step is to determine if your change is "Breaking" or "Non-Breaking."
A. Making a Non-Breaking Change
A non-breaking (or backward-compatible) change does not violate the contract for existing consumers of the schema.
Examples of Non-Breaking Changes:
- Adding a new optional field.
- Adding a new value to an
enum(provided consumers are built to handle unknown values gracefully). - Adding a non-required property to an object.
How to Implement:
- Locate the schema file in the latest existing version folder (e.g.,
src/effect/v1/main.json). - Apply your changes directly to that file.
- Commit your changes. No new folders or files are needed.
B. Making a Breaking Change
A breaking (or backward-incompatible) change will cause existing data or clients to fail validation. We must introduce a new version to handle these changes safely.
Examples of Breaking Changes:
- Removing a field.
- Renaming a field.
- Changing a field's data type (e.g.,
stringtointeger). - Making an optional field required.
- Adding a new required field (with no default value).
- Adding a new validation rule (e.g., changing a string's
maxLength). - Removing a value from an
enum.
How to Implement:
This process establishes the next global version for our schemas (e.g., v2).
-
Identify the Schema: Locate the schema you need to modify (e.g.,
src/effect/v1/main.json). -
Create New Version Directory: Create a new version directory alongside the existing one. If you are changing a
v1schema, you will create av2directory.mkdir -p src/effect/v2
-
Copy-on-Write: Copy the schema from the previous version into the new directory. Do not move it.
cp src/effect/v1/main.json src/effect/v2/main.json
-
Apply Your Change: Make your breaking change to the newly copied file (
src/effect/v2/main.json). -
Leave the Old Version Untouched: The original
src/effect/v1/main.jsonmust not be modified.
Workflow in Practice
Let's walk through a development cycle where multiple changes occur. Our current latest version is v1.
Scenario 1: A breaking change is needed for effect schemas.
- You need to rename the
source_urlfield tosourceUrlinsrc/effect/v1/main.json. - Action: You create
src/effect/v2/, copymain.jsoninto it, and make the change there. This officially starts the "v2" generation of our schemas.
Scenario 2: A month later (in the same release cycle), a breaking change is needed for layers.
- You need to change the
opacityfield insrc/layers/v1/background.jsonfrom an integer0-100to a float0.0-1.0. - Action: This change is part of the same global
v2update. You createsrc/layers/v2/, copybackground.jsoninto it, and apply the change there.
Result:
The global v2 version now consists of:
src/effect/v2/main.json(new)src/layers/v2/background.json(new)src/story/v1/timeline.json(fallback fromv1, since nov2exists)
A v3 would only be created when we need to make a new breaking change to the already-released v2 set of schemas.
Guiding Principles (TL;DR)
- Always check if your change is breaking. When in doubt, treat it as a breaking change.
- To add an optional field, edit the latest version in place.
- To make any other change, create a new version folder and copy the file.
- Communicate with other teams when a new schema version is being introduced.