# \[dry-schema\] Evolve support

**URL:** <https://discourse.dry-rb.org/t/dry-schema-evolve-support/1933>\
**Category:** Ideas\
**Created:** [July 16, 2025, 6:23pm UTC](https://discourse.dry-rb.org/t/dry-schema-evolve-support/1933 "2025-07-16T18:23:38Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![baweaver](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/baweaver/32/1043_2.png) [@baweaver](https://discourse.dry-rb.org/u/baweaver)\
**Post date:** [July 16, 2025, 6:23pm UTC](https://discourse.dry-rb.org/t/dry-schema-evolve-support/1933/1 "2025-07-16T18:23:38Z")

</div>

Currently, and understandably, dry-schema generated schemas are immutable. A use case I have is versioned schemas to where I want to do things like update, add, or remove fields and keep them semantically locked, and while I do not think that entire feature is worth supporting for this core library there is a concept from the Haskell / Rambda world around “evolve” which is basically a fancier “merge” operation.

What I’m proposing is that if we have a schema, say:

```ruby
UserSchema = Dry::Schema.Params do
  required(:name).filled(:string)
  required(:email).filled(:string)

  required(:age).maybe(:integer)
end

```

…that we could _evolve_ it into this:

```ruby
UserSchemaV2 = UserSchema.evolve do
  optional(:email).filled(:string)
  remove(:age)
end

```

…in which the original attributes are preserved but it introduces deltas from the previous schema. I’m not stuck on the name mind, as much as the concept.

Other nice-to-haves here might be things like tracking those deltas and marking it as an evolved schema to aid in aforementioned potential versioning.

I may be willing to build a prototype for this but wanted to run it by as an idea first.

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [July 17, 2025, 7:59pm UTC](https://discourse.dry-rb.org/t/dry-schema-evolve-support/1933/2 "2025-07-17T19:59:20Z")

</div>

I have some qualms about mutating schema in this way, from personal experience: the more alterations you pile up, the harder it becomes to understand what the final product will be. For this reason I prefer a more declarative approach to schema versioning.

Are you aware of schema composition as an option?

```ruby
module UserSchema
  Baseline = Dry::Schema.Params do
    required(:name).filled(:string)
    required(:email).filled(:string)
  end

  V1 = Dry::Schema.Params(parent: Baseline) do
    required(:age).maybe(:integer)
  end

  V2 = Dry::Schema.Params(parent: Baseline) do
    optional(:email).filled(:string)
  end
end

```

The `parent:` option supports multiple inheritance by passing an array of schemas.

The only thing this would not support is _removal_ of a key; but you can model this differently by moving the definition of the key, or isolating it into a separate schema that is inherited into a subset of versions.
