Skip to content

Migration Adapter

Software tool / conversion bridge — instantiates Round-Trip Code Alignment

Converts content encoded under an old scheme into a newer one on the fly, carrying over what maps cleanly and reporting the fields that don't.

When a scheme changes but a mountain of content is already encoded under the old one, the round trip breaks: a new reader meets old bytes it can no longer interpret. A Migration Adapter repairs that break rather than preventing it. It sits between an old encoded form and a new reader — or an old store and a new schema — and rewrites the representation across the version gap: renaming fields, splitting or merging them, supplying defaults. Its defining move, the thing that separates it from a blunt converter, is that it makes the loss explicit: whatever cannot be carried faithfully is reported or quarantined, never silently dropped. Where a Version Negotiation Handshake stops a mismatch from ever occurring, the adapter is what you reach for once the mismatch already exists.

Example

A SaaS company changes how it stores customer addresses. The old scheme (v1) kept a single freeform address string; the new one (v2) splits it into street, city, region, and postal_code. Millions of records are already written as v1. A Migration Adapter maps v1 to v2, parsing each freeform string against the new fields. Where it can split confidently, it does. Where a record is ambiguous — no recognizable postal code, an unparseable format — it does not guess: it flags the record as non-preserved and routes it to a review queue, so the roughly 3% that can't cross cleanly stay visible rather than becoming quiet data loss.

Rather than a risky one-shot rewrite, the team runs it as migrate-on-read under an expand-and-contract rollout,[1] so v1 and v2 coexist while the new reader is proven out — the same shape of mapping an ORM performs when its object model outgrows the columns underneath it.

How it works

  • Hold a directional field mapping from the old scheme to the new one — rename, split, merge, default, drop — as the executable form of the version-to-version relationship.
  • Transform the encoded form, not the live objects. The adapter reads a serialized/stored representation in scheme A and emits one in scheme B; it operates entirely on encoded artifacts.
  • Emit a non-preservation report. Anything with no faithful target in the new scheme is surfaced — quarantined, flagged, or blocked — as a first-class output, not a swallowed exception.
  • Apply eagerly or lazily. Run once in bulk, or convert on read so old and new can coexist during a staged cutover.

Tuning parameters

  • directionality — one-way A→B vs. bidirectional A↔B. Two-way keeps the full round trip alive but forces you to confront every mapping that can't be inverted.
  • non-preservation handling — drop-and-report vs. quarantine vs. hard-block. Stricter loses less but demands more manual cleanup.
  • timing — bulk one-shot vs. migrate-on-read vs. dual-write. Lazy conversion spreads cost and preserves a rollback path; bulk is cleaner but riskier.
  • confidence threshold — how sure a heuristic split or merge must be before it is applied rather than flagged. Higher thresholds mean fewer bad conversions and more review.
  • mapping coverage — how many field relationships to encode. Fuller mappings carry more across, but cost more to build and keep current.

When it helps, and when it misleads

Its strength is that it lets a system evolve its scheme without abandoning the data already written under the old one, and it makes migration loss auditable instead of invisible.

Its failure modes cluster around loss that slips past the report: a mapping that quietly defaults or drops a field the consumer still needed — a lossy migration wearing a clean face — and chained adapters (v1→v2→v3) whose small approximations compound into real drift. Then there is the invertibility trap: a one-way adapter silently breaks the round trip the moment anyone still needs to read the old form. The classic misuse is shipping the adapter as a permanent crutch so the old scheme is never actually retired, or running it to "prove" a migration was clean when nobody read the non-preservation report. The discipline that keeps it honest is to treat that report as a primary output, set a real sunset for the old scheme, and test the adapter with round-trip vectors rather than forward conversion alone.

How it implements the components

  • version_compatibility_rule — the adapter is the executable form of the rule relating two scheme versions; running it is what makes old content compatible with a new reader.
  • encoded_form — it consumes the encoded form under the old scheme and emits it under the new one, working end to end on encoded representations.
  • decode_failure_handling — its signature move: reporting and quarantining the fields and records that don't map, instead of dropping them.

It does not implement the base encode/decode of a single scheme (encoder_function, decoder_function) — that is the Parser/Emitter Pair — nor the connect-time agreement on a version (shared_scheme_contract) — the Version Negotiation Handshake — nor the declarative table of which versions interoperate, which is the Compatibility Matrix.

  • Instantiates: Round-Trip Code Alignment — it keeps the round trip working across a scheme change instead of letting a version bump break it.
  • Consumes: Schema Registry — supplies the old and new scheme definitions the adapter maps between.
  • Sibling mechanisms: Version Negotiation Handshake · Compatibility Matrix · Schema Registry · Codec Specification · Canonicalization Rule · Decode Error Taxonomy · Checksum or Hash Validation · Round-Trip Property Test · Golden Test Vectors · Lossy Compression Profile · Parser/Emitter Pair

Notes

A Migration Adapter is one-directional by construction, so keeping the round trip alive across a version boundary usually needs the inverse adapter too — and the fields its report flags as non-preserved are exactly the ones that cannot be inverted. The non-preservation report is therefore also the map of where the round trip breaks.

References

[1] The expand-and-contract (a.k.a. parallel-change) migration pattern: add the new scheme alongside the old, migrate and dual-run, then retire the old once nothing depends on it. It is the standard way to change a stored scheme without a risky big-bang cutover, and it is why an adapter is so often deployed to convert on read rather than all at once.