Skip to content

Schema migration system for ZIO Schema 2#974

Closed
Godzilla675 wants to merge 14 commits intozio:mainfrom
Godzilla675:schema-migration-for-zio-schema-2-system
Closed

Schema migration system for ZIO Schema 2#974
Godzilla675 wants to merge 14 commits intozio:mainfrom
Godzilla675:schema-migration-for-zio-schema-2-system

Conversation

@Godzilla675
Copy link
Copy Markdown
Contributor

@Godzilla675 Godzilla675 commented Feb 4, 2026

Summary

  • Implement pure, serializable migration core for ZIO Schema 2 (DynamicMigration + MigrationAction + DynamicSchemaExpr schemas).
  • Add Schema.structural derivation for structural records/variants and structural bindings (Scala 3).
  • Add selector macros + builder syntax for optic-style paths (Scala 2/3).
  • Add build-time validation, DefaultValue resolution, correct transform context, and extended runtime path traversal.

Examples

Structural schema + addField

type PersonV1 = { def name: String }
@schema case class PersonV2(name: String, age: Int)

implicit val v1Schema: Schema[PersonV1] = Schema.structural[PersonV1]

val v1ToV2 =
  MigrationBuilder[PersonV1, PersonV2]
    .addField(_.age, 0)
    .build

Rename + composition

@schema case class PersonV3(fullName: String, age: Int)

val v2ToV3 =
  MigrationBuilder[PersonV2, PersonV3]
    .renameField(_.name, _.fullName)
    .buildPartial

val v1ToV3 = v1ToV2 ++ v2ToV3

Selector grammar (paths)

MigrationBuilder[Old, New]
.dropField(.address.street)
.optionalizeField(
.email)
.buildPartial

Notable behavior

  • build() validates structural changes (including optionality) by simulating actions.
  • DefaultValue resolves at build time (forward strict; reverse best-effort).
  • Typed Migration.apply returns a MigrationError for structural-only schemas; use applyDynamic.

Tests

  • sbt -no-colors "schemaJVM/test" "schemaJS/test"

Demo / Video

migration-demo.mp4

Notes / limitations

  • Scala 2 cannot derive Schema.structural for true structural/union types.
  • Typed apply for structural schemas is intentionally unsupported (use applyDynamic).

Review guide

  • Serialization schemas: schema/shared/src/main/scala/zio/blocks/schema/migration/MigrationSchemas.scala
  • Structural derivation: schema/shared/src/main/scala-3/zio/blocks/schema/SchemaCompanionVersionSpecific.scala (+ bindings)
  • Selector macros + syntax: schema/shared/src/main/scala-2|3/zio/blocks/schema/migration/*
  • Validation: schema/shared/src/main/scala/zio/blocks/schema/migration/MigrationValidator.scala
  • Tests: schema/shared/src/test/scala/zio/blocks/schema/migration/*

Issues

Copilot AI review requested due to automatic review settings February 4, 2026 17:03
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Feb 4, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ Godzilla675
❌ Ahmed


Ahmed seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a pure, algebraic schema migration system for ZIO Schema 2 that represents structural transformations between schema versions as first-class, serializable data. The system provides type-safe migrations with compile-time validation, reversibility, and path-based error reporting.

Changes:

  • Core migration types: Migration[A,B], DynamicMigration, MigrationAction ADT, and DynamicSchemaExpr for serializable value transformations
  • Structural schema derivation for Scala 3 (refinement types and union types) with runtime restrictions
  • Type-safe selector macros and builder syntax for ergonomic path construction in both Scala 2 and 3
  • Build-time validation via MigrationValidator and default value resolution in MigrationBuilder
  • Comprehensive test coverage (1600+ tests) across all migration operations

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated no comments.

Show a summary per file
File Description
MigrationAction.scala ADT defining 15+ migration actions (AddField, DropField, RenameField, etc.) with structural reversibility
DynamicMigration.scala Untyped migration interpreter operating on DynamicValue with comprehensive path traversal
Migration.scala Typed wrapper providing type-safe apply with error handling for structural schemas
DynamicSchemaExpr.scala Serializable expression DSL supporting arithmetic, string ops, type coercion, and path navigation
MigrationBuilder.scala Fluent builder API with strict/best-effort default value resolution at build time
MigrationValidator.scala Structural validation by simulating actions and comparing schema structures
MigrationError.scala Path-aware error types with detailed diagnostic messages
Selector.scala Type-safe selector abstraction for composable paths
SelectorMacros.scala (2+3) Compile-time conversion of lambda expressions to DynamicOptic paths
MigrationBuilderSyntax.scala (2+3) Extension methods enabling selector syntax on MigrationBuilder
SchemaCompanionVersionSpecific.scala Scala 3 structural derivation for refinement/union types; Scala 2 throws unsupported
Binding classes StructuralConstructor/Deconstructor/Matcher that throw at runtime (compile-time only)
JsonBinaryCodecDeriver.scala Delayed register initialization fix for recursive record encoding
Test utilities Newline normalization for cross-platform test consistency
Test specs 10+ comprehensive test files covering all actions, errors, validation, and serialization
MigrationSchemas.scala Hand-written schemas for serializing migration types
docs/reference/migration.md Complete API documentation with examples and best practices
package.scala Package documentation describing key types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Godzilla675 and others added 7 commits February 4, 2026 19:29
…calls

- Replace typeName with typeId in structural derivation macros
- Use TypeId.nominal for structural types
- Fix DynamicValue.Record to use varargs or Chunk instead of Vector
- Fix MigrationValidator to use typeId.name instead of typeName.name
- Fix scalafmt issues in Scala 2 and Scala 3 migration files
- Fix Scaladoc @throws annotation to use fully qualified name
- Add MigrationValidatorSpec with 44 comprehensive tests
- Add 25+ error path tests to DynamicMigrationSpec
- Total migration tests increased from ~94 to ~166
- Add error aggregation tests for AtIndices, AtMapKeys, Elements, MapKeys, MapValues
- Add DynamicSchemaExpr tests: StringConcat, StringLength, CoercePrimitive, Arithmetic ops
- Add Logical/Relational operator tests
- Add navigateDynamicValue coverage tests
- Add Wrapped, AtMapKey, Case path navigation edge case tests
- Add MigrationValidator path navigation tests for all optic types
- Increase branch coverage to meet 80% minimum requirement
@Godzilla675
Copy link
Copy Markdown
Contributor Author

Superseded by #982

@Godzilla675 Godzilla675 closed this Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants