MIGRATION DIARYARCHITECTURE

Migration diary: evolving an API without versioning it

Retrofitting evolving an API without versioning it onto a public sector casework platform that wasn't allowed to stop.

FILED
READ
AUTHOR
REF

Greenfield advice is easy to write and not much use. This is the same idea applied to a system already carrying production load that isn't allowed to stop, with the compromises left in view.

A version number is a promise to maintain two systems. Most changes don't need one. They need additive fields, tolerant readers, and the discipline to never repurpose a name. Save the version bump for the change that genuinely can't be made compatibly.

ADDITIVE IS ALMOST ALWAYS AVAILABLE

New optional field, new endpoint, new value in an enum that clients already handle by ignoring what they don't recognise. The overwhelming majority of real changes fit one of those shapes, and none of them break anybody.

What breaks people is subtler: tightening validation, making an optional field required, changing what a value means while keeping its name. That last one is the worst because nothing fails loudly. The caller carries on, now wrong.

NEVER REPURPOSE A NAME

If the meaning changes, the name changes. `status` meaning something new in March than it did in February is a bug you've distributed to every consumer, and it will be found by a customer rather than a test.

Deprecate by addition. Add the new field, populate both, tell consumers, then measure whether anyone still reads the old one. That last part needs instrumentation, which is the step that makes removal possible rather than theoretical.

The code wasn't the hard part. Convincing caseworkers that a second reader wouldn't cause a statutory clock was missed because the case had two owners was, because the last project that promised that did exactly that.

WHEN YOU DO HAVE TO BREAK IT

Sometimes the model was wrong and no amount of additive change fixes it. Then version, and version the whole surface rather than one endpoint, because a client shouldn't have to track which parts are on which version.

And set the retirement date when you launch the new one, with the old one emitting a warning your consumers can see. A version with no end date isn't a migration, it's a second product.

We ran both paths against live traffic for three weeks and compared every case record. The mismatch rate started at four percent, all of it the old system's undocumented rounding.

WHERE IT GOES WRONG

  • Reusing a field name with a new meaning, so consumers keep working and quietly go wrong.
  • Making an optional field required, and finding out from a customer's integration.
  • Deprecating an endpoint with no instrumentation, so nobody can prove it's safe to remove.
  • A v1 that never dies, because nothing forced the conversation.

Add, don't repurpose. If the meaning changes, so does the name.

WHAT THE MIGRATION COST

Eleven weeks, one reverted step, nothing customer-visible. The reverted step was the one where we changed two things at once. We keep relearning that and we keep writing it down.

RELATED
SAME GROUND, DIFFERENT ANGLE
ALL TRANSMISSIONS