SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Architecture / 4 MIN READ

How do you evolve a configuration schema while old and new binaries read and write the same config?

A practical guide to evolving shared configuration safely in mixed-version fleets, covering additive changes, defaults, downgrade support, and rollout order.

Treat shared config as a compatibility contract, not just a file format. If old and new binaries will both read or rewrite the same document, the safe path is to keep schema changes additive, make presence-versus-default explicit, preserve unknown fields on round-trip, and delay any new write format until every possible writer can tolerate or preserve it. If rollback matters, design downgrade behavior before release.

What contract should you define before changing the schema?

Write down four rules first: who reads the config, who can write it back, whether humans depend on comments or ordering, and whether a partial rollout can be rolled back after new binaries have already rewritten the file.

Also separate parse compatibility from behavior compatibility. A document can still parse while changing meaning because a default changed, a unit changed from seconds to milliseconds, or an unset value now means something different. JSON Schema defines structure, constraints, and related processing for JSON data, but it does not define your rollout semantics or downgrade policy.

The key operational boundary is the write path. During a mixed-version rollout, keep the canonical config in the old writable shape until every binary that might rewrite that config has been upgraded. A preflight compatibility check is useful, but it is not enough if an older writer can still publish or normalize the file afterward.

How should the format evolve without breaking mixed versions?

Prefer additive changes and stable identifiers. In protobuf, field identity is tied to its number, and the proto3 language guide says field numbers cannot be changed once a message type is in use and should never be reused. Even if your on-disk config is JSON or YAML, the lesson holds: do not quickly recycle names, meanings, or enum values.

Be especially careful with defaults. The same proto3 guide recommends optional for maximum compatibility and explains that explicit presence distinguishes an unset field from one set to a default value. That is exactly the distinction configuration systems lose when they collapse absence into false, 0, or empty string. If behavior changes based on whether an operator set a value intentionally, model that state directly.

YAML adds another risk: it is a full data serialization language, not just pretty key-value text. The YAML 1.2.2 specification describes multiple information models and includes comments, anchors, aliases, styles, and mapping key order in its presentation and serialization model. A parser/emitter that reloads and rewrites the whole file may preserve data values while still dropping comments or normalizing structure in ways operators dislike. If humans author the file, separate machine-owned state from human-owned config unless your tooling can round-trip safely.

What rollout pattern is safe when versions coexist?

Use read-old/write-old first, then read-old-and-new/write-new only after all writers are upgraded.

A concrete example: rename timeout_ms to request_timeout_ms.

  • Release N: readers accept both keys; writers emit only timeout_ms.
  • After every writer is upgraded: writers emit both keys and reject mismatched values.
  • After the downgrade window closes: writers emit only request_timeout_ms; readers may still accept the old key for one release.

This is the same core idea Kubernetes uses for versioned custom resources: CustomResourceDefinitions can serve multiple versions during migration. When representations differ, Kubernetes supports explicit conversion; when schemas do not change, the default None conversion strategy can serve different versions by changing apiVersion. The same Kubernetes documentation also says, “It is perfectly safe for some clients to use the old version while others use the new version”.

What should you test before shipping?

Test compatibility as a matrix, not a unit test.

Checklist:

  • Old binary reads new config without crashing.
  • Old writer does not drop unknown fields you need to keep.
  • New binary preserves absence versus explicit zero or false.
  • New config can be rolled back safely, or rollback is explicitly unsupported.
  • Round-trip tests compare old→new→old and new→old→new on golden files.
  • Policy checks block unsafe changes such as renamed required fields or changed units.

Should you add a schema version field?
Yes, when it selects a converter or blocks an unsafe downgrade. Do not rely on it alone; real compatibility still depends on reader and writer behavior.

Should multiple services write the same canonical config during rollout?
Prefer one canonical writer or a separate migration tool. The more writers you have, the harder it is to enforce the upgrade boundary that keeps new state from being erased.

If you are planning a schema change this week, start by listing every reader and every writer, then decide which version must remain the writable shape until the rollout and rollback windows both close.

Reviewed: 2026-09-06

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗