Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migrating a Configuration Schema

The field was url, the new code wants endpoint, and the store still serves the old document. What happens, and the choreography that makes it a non-event. There is no migration engine here, on purpose — deserialize-fails→last-known-good already bounds the blast radius, and a migration DSL would be a second schema language to get wrong.

What happens with no plan

New code deploys; the old document fails to deserialize; the process that was running keeps serving last-known-good; a process that restarts fails init (correctly — see Readiness). So: nothing burns instantly, and nothing converges either. Plan.

The tools already in the box

  • Aliases — the built-in answer for renames: builder.alias("url", "endpoint") lets the old spelling in the document fill the new field. Ship it in the same release that renames the field, delete it two releases later. Most migrations end here.
  • Option<T> + default — the answer for additions: a new field the old documents lack must be optional or defaulted for one transition window.

The choreography for shape changes

When a rename will not do (a field splits, a table restructures), run the three-step dance — consumers first, always:

1. consumers accept BOTH        (enum or untagged struct over V1|V2,
                                 or schema_version: u32 + branch)
2. producers switch to V2       (the store's document changes)
3. consumers drop V1            (next release, delete the branch)

A schema_version field costs one line and turns "which shape is this?" from inference into a statement:

[app]
schema_version = 2

The one rule

Never make step 2 before step 1 is fully deployed — the store is shared state, and a document only V2-readers understand bricks every V1 process that restarts. This is the same contract as any rolling data migration; configuration is data.