Migrations
How Laizy schema sync works — migration plans, impact analysis, dry-run mode, and handling destructive changes safely.
Migrations
When you modify your .laizy schema and run laizy sync, the CLI compares your local schema against the current database state and generates a migration plan. This plan describes every change that needs to happen, classifies each by safety level, and asks for confirmation before applying anything destructive.
How Schema Sync Works
The sync process follows a five-step workflow:
Parse Local Schema
The CLI reads your laizy/schema.laizy file and parses it into an AST (Abstract Syntax Tree) containing all your model definitions, fields, and constraints.
Fetch Remote State
The CLI calls the Laizy API to retrieve the current content models stored in the database for your project.
Compute Diff
The Schema Differ compares local models against remote models and produces a diff with three categories:
- To Create -- Models in your local schema that do not exist in the database
- To Update -- Models that exist in both but have field or constraint differences
- To Delete -- Models in the database that are no longer in your local schema
Generate Migration Plan
The Migration Planner takes the diff and produces a plan with operations, impact classifications, and a safety report. Each operation is tagged as safe, warning, or destructive.
Execute or Preview
If --dry-run is set, the CLI displays the plan without executing. Otherwise, it shows the plan and asks for confirmation before applying changes. Content impact analysis enriches the plan with counts of affected entries.
Running Migrations
Preview changes with dry-run
Always preview before applying:
pnpm laizy sync --dry-runThis shows the full migration plan -- which models will be created, updated, or deleted -- without making any changes.
Interactive sync (default)
The standard sync command shows the plan and asks for confirmation:
pnpm laizy syncFor warning and destructive changes, you will see a detailed impact summary and be prompted to confirm. The confirmation message changes based on severity:
- Warning changes: "Continue with migration?"
- Destructive changes: "Are you absolutely sure you want to continue? This cannot be undone."
Force sync (skip confirmation)
For CI/CD pipelines or scripts where interactive prompts are not possible:
pnpm laizy sync --forceUsing --force skips all confirmation prompts, including for destructive changes. Use this flag carefully and only in automated environments where the migration has already been reviewed.
Impact Classification
Every migration operation is classified into one of three impact levels:
Safe
No data loss or content disruption. These operations execute without requiring confirmation.
| Operation | Example |
|---|---|
| Creating a new model | Adding a Product model to your schema |
| Adding an optional field | New subtitle: String field |
| Adding a field with a default | New status: String { default: "draft" } |
| Relaxing constraints | Removing required from a field |
Changing maxLength | Adjusting character limits |
Warning
Changes that may need attention. Existing content might need updates, but data is not lost. Confirmation is required.
| Operation | Example |
|---|---|
| Adding a required field without a default | New title: String { required: true } on an existing model |
| Making an optional field required | Changing from bio: String to bio: String { required: true } |
| Compatible type changes | Int to Float, or String to Int |
When a required field is added without a default, the migration planner generates an auto-fill strategy that populates existing entries with a type-appropriate default value:
| Field Type | Auto-fill Value |
|---|---|
String | "" (empty string) |
Int | 0 |
Float | 0.0 |
Boolean | false |
DateTime | null |
Destructive
Changes that cause permanent data loss. Strong confirmation is required, and the CLI shows explicit warnings.
| Operation | Example |
|---|---|
| Deleting a model | Removing model Author { ... } from your schema |
| Removing a field | Deleting a field from a model |
| Incompatible type changes | Boolean to String, DateTime to Int |
Migration Plan Output
When you run laizy sync, the CLI displays a table summarizing each operation:
Migration Plan:
Operation Model Impact Description
──────────── ──────── ────── ───────────
create_model Product safe Create new content model "Product" with 4 fields (no existing content)
update_model BlogPost warning Update model "BlogPost" (2 field changes) (15 content entries affected)
delete_model OldPage destructive Delete content model "OldPage" and all associated content (3 content entries affected)The plan includes content impact counts showing how many existing entries will be affected by each change. This helps you understand the scope of the migration before confirming.
Content Impact Analysis
Before executing a migration, the orchestrator queries the API to count how many content entries exist for each affected model. This data enriches the migration plan:
- No existing content: Changes are effectively risk-free regardless of impact level
- Content exists: The count is shown alongside each operation so you can assess the real-world impact
For example, deleting a model with 0 entries is technically destructive but practically harmless. Deleting a model with 500 entries deserves careful consideration.
The Schema Sync Pipeline
Under the hood, three components work together:
Schema Differ
Compares local and remote model definitions field by field. For each model that exists in both, it produces a list of FieldChange objects categorized as added, removed, or modified.
Each field change includes:
- Impact level:
safe,warning, ordestructive - Migration strategy:
auto_fill,manual, ornone - Default value: For auto-fill strategies, the value to use
Migration Planner
Takes the diff and generates an ordered list of MigrationOperation objects. It also produces a SafetyReport summarizing:
- Count of safe, warning, and destructive operations
- Warning messages for operations that need attention
- Whether confirmation is required
Schema Sync Orchestrator
Coordinates the full workflow. It calls the differ, the planner, enriches the plan with content impact data from the API, handles dry-run logic, and executes the migration through the Management Client.
const orchestrator = new SchemaSyncOrchestrator(managementClient);
const result = await orchestrator.syncSchemas(localModels, {
projectId: 'your-project-id',
dryRun: false,
force: false,
});Migration Strategies
When the planner detects a change that affects existing content, it assigns a migration strategy:
| Strategy | When Used | What Happens |
|---|---|---|
auto_fill | Adding required field, making field required | Existing entries are backfilled with a default value |
manual | Incompatible type changes | You need to manually update data before or after migration |
none | Safe changes, optional fields | No data transformation needed |
Best Practices
-
Always dry-run first. Run
laizy sync --dry-runbefore every sync to understand what will change. -
Add defaults to new required fields. When adding a required field to a model that already has content, include a
defaultvalue to make the migration safe:// Safe migration — default value provided category: String { required: true default: "uncategorized" } -
Avoid removing fields in production. If you need to deprecate a field, consider making it optional first, then removing it in a later migration after confirming the data is no longer needed.
-
Use force mode only in CI/CD. The
--forceflag is designed for automated pipelines where the migration plan has already been reviewed via--dry-runin a previous step. -
Check content counts. The migration plan shows how many entries are affected. Zero-content models are safe targets for schema experimentation.
Next Steps
- Sync Command -- CLI flags and options for the sync command
- Field Types -- All field types and their migration behaviors
- Constraints -- How constraints affect migration safety