Data contracts: producer ↔ consumer без сюрпризов
Schema versioning, политика breaking changes, валидация в pipeline, choice block vs warn, Avro/Protobuf/JSON Schema на практике.
Спроектируй data contract между {{producer}} и {{consumers}}. Транспорт: {{transport}}.
Зачем контракт
Без контракта producer катит «безобидное» изменение — переименовал поле, поменял тип, выкинул enum-значение — и downstream падают на проде через сутки, потому что schema-on-read узнала об этом первой. Контракт переносит проблему влево, на CI producer'а.
Что входит в контракт
- Schema — поля, типы, optionality, defaults.
- Semantics — что значит каждое поле, единицы, timezone, enum-значения.
- SLA — частота, задержка, объём, доступность.
- Owner — команда + on-call, не email-алиас.
- Versioning policy — что breaking, что нет.
- Validation — где валидируется и что происходит при нарушении.
Schema format — выбор
| Формат | Плюсы | Минусы | Когда | | Avro | Schema registry, evolution rules встроены | Бинарный, читать глазами больно | Kafka streaming | | Protobuf | Языковая поддержка, версионирование полей по номеру | Строже эволюция | gRPC, межсервисное | | JSON Schema | Человекочитаемо, валидаторы везде | Эволюция — на тебе | REST API, файлы | | Iceberg/Delta | Schema evolution в storage | Привязка к платформе | Lakehouse tables |
Правило: формат source-of-truth — Avro или Protobuf. JSON Schema — для документации API.
Политика breaking changes
Не breaking (safe):
- Добавить optional поле с default
- Добавить новое enum-значение (если consumer его игнорирует unknown-routed)
- Добавить новый event type в топик
Breaking (требует версии):
- Удалить или переименовать поле
- Сменить тип (int → string)
- Сделать optional обязательным
- Удалить enum-значение
- Изменить семантику поля (была «cents» — стала «dollars»)
Версионируй namespace: events.orders.v1.OrderPlaced. v2 живёт параллельно. Старая версия deprecate-ится с deadline.
Валидация в pipeline
Три точки:
- Producer CI — schema linter, compatibility check против registry. Block merge при breaking.
- Producer runtime — каждое сообщение валидируется до публикации. Невалидное → DLQ + alert.
- Consumer ingestion — повторная валидация (defence in depth). Невалидное → quarantine table.
Block vs Warn
- Block при breaking change в schema registry — producer не может задеплоиться.
- Warn при появлении нового optional поля — consumer узнаёт, но не падает.
- Block при превышении SLA на задержку — page on-call.
- Warn при росте volume на 50% — Slack-сообщение.
Tooling stack (пример)
Schema source: .avsc / .proto в репо producer
Registry: Confluent Schema Registry / Buf Schema Registry
CI check: buf breaking, avro-tools compatibility
Runtime validation: producer SDK (avro-python, protobuf-go)
Consumer side: schema-aware deserialiser + DLQ
Observability: schema_violations metric, contract_age dashboard
Документация
Каждый contract имеет:
- README в репо: владелец, slack-канал, on-call, примеры
- Generated docs из schema (комментарии в .proto → markdown)
- Changelog: что изменилось, когда, breaking ли
Anti-patterns
- ❌ "JSON-as-contract" без schema — типы плывут, optional vs required размывается
- ❌ Полагаться на producer'а «он не сломает» — без CI-check сломает в течение квартала
- ❌ Менять семантику поля без переименования (cents → dollars в том же
price) — schema валидна, данные битые - ❌ Один топик на несколько event types без discriminator field — consumer не знает, что десериализовать
- ❌ Deprecate старой версии без deadline — она будет жить вечно, оба варианта надо поддерживать
Мета-теги и schema.org для страницы
Полный набор метаданных для страницы: title, description, OG, Twitter, JSON-LD.
Стратегия Schema.org разметки
Article / Product / BreadcrumbList / FAQ / HowTo: что внедрить, какие поля обязательны, как валидировать.
Дизайн схемы БД
Таблицы, отношения, ключи, индексы — схема которую легко эволюционировать.