Schema Registry and Evolution

In a decoupled architecture (e.g., Kafka, gRPC), the "contract" between services is the schema. A Schema Registry acts as the single source of truth and the gatekeeper for evolution, ensuring that a producer cannot ship a change that crashes its consumers.

The Compatibility Matrix

When updating a schema, you must select a compatibility mode. This decision dictates whether you update consumers or producers first.

ModeWho can read what?Update Order
BackwardNew consumer can read old data.Consumers first.
ForwardOld consumer can read new data.Producers first.
FullBoth are true.Any order.
NoneNo checks.Dangerous; requires coordinated downtime.

Practical Evolution Rules (Avro / Protobuf)

Protobuf Field ID Discipline

Protobuf relies on integer tags, not field names. Renaming user_name to username is fine; changing tag 1 to tag 2 is a catastrophic failure.

message User {
  // Field 1 was removed in v2.0. DO NOT REUSE THE ID.
  reserved 1; 
  reserved "old_field_name";

  string username = 2; // Use ID 2
  int32 age = 3;
}

The Schema Registry Workflow

  1. Producer attempts to register Schema v2.
  2. Registry checks v2 against v1 using the configured compatibility rule (e.g., BACKWARD).
  3. Registry rejects the schema if it contains a breaking change (e.g., adding a required field without a default).
  4. Consumer fetches v2 from the registry by ID when it encounters a message it doesn't recognize.

Tooling Landscape

Breaking Changes in Production

If you MUST make a breaking change:

  1. Create a new topic or a new versioned endpoint (e.g., /v2/).
  2. Run a "bridge" service that consumes from the old topic, transforms data, and publishes to the new topic.
  3. Gradually migrate consumers to the new topic.

Further Reading