Schema Registry Design
A pragmatic blueprint for designing a schema registry that scales across services, languages, and teams. This recipe walks through topology choices, evolution rules, and runtime contracts that keep producers and consumers honest without slowing anyone down.
1. Pick a topology before you pick a format
Centralized registries (one cluster, one source of truth) win for governance but become a blast radius. Federated registries trade consistency for autonomy. Start centralized for the first 50 schemas, then federate per business domain once team boundaries harden. The format (Avro, Protobuf, JSON Schema) matters less than the discipline around versioning.
- Centralized: strong consistency, single audit log, easier compatibility checks.
- Federated: domain isolation, regional latency wins, harder cross-domain joins.
- Hybrid: central catalog, federated storage, signed manifests glue them together.
2. Encode compatibility as policy, not folklore
Every subject needs a declared compatibility mode at creation time. The registry enforces it on every write. Folklore ("just don't rename fields") breaks the moment a new engineer ships a producer at 3am. Codify the rules and reject violators at the API layer.
POST /subjects/orders.v1/versions
{
"compatibility": "BACKWARD_TRANSITIVE",
"schema": { "type": "record", "name": "Order", ... },
"owner": "team-checkout",
"deprecates": null
}
# Registry rejects with 409 if the new schema
# breaks BACKWARD_TRANSITIVE against any prior version.3. Treat the registry as a runtime dependency
Producers and consumers should fetch schemas at boot, cache them locally, and refuse to start on a cache miss in production. Sidecar agents make this cheap. Never embed schemas in application binaries — you lose the audit trail and gain a deploy bottleneck. Ship a small client that handles retries, ETag caching, and signed responses.
Producer side
Pin schema ID per topic. Fail loudly if the registry is unreachable on first publish.
Consumer side
Lazy-fetch by ID embedded in the payload. Cache forever — IDs are immutable.