# ADR-0005: Protocol Buffers for event schemas, OpenAPI for HTTP ## Status Accepted — 2026-04-13 ## Context Two contract surfaces exist: 1. **HTTP** — synchronous, client ↔ server, human-readable debugging matters. OpenAPI is the default and generates decent TS clients. 2. **Events** — durable, fan-out to ML consumers, schema evolution critical. Feature pipelines trained on old schemas will silently misbehave when producers change a field. Using OpenAPI for both means: - Python pydantic generation is awkward and hand-maintained in practice. - No wire-format discipline (JSON is loose). - No central schema registry, so schema drift is undetected until a model regresses. ## Decision - **HTTP** contracts: OpenAPI 3.1 in `packages/shared-types/http/`. Generate TS clients; hand-write Python pydantic models for ML consumers (few, and they're shallow). - **Event** contracts: Protocol Buffers in `packages/shared-types/events/`. Generate TS and Python. All events carry an envelope: `{event_id, occurred_at, schema_version, producer, payload}`. - **Schema registry:** lightweight self-hosted (buf.build Schema Registry OSS or a tiny registry in `events/`). CI check blocks breaking changes without a version bump. - **Evolution rules:** additive only within a major version; `reserved` for removed fields; new `schema_version` for breaking changes; consumers advertise the versions they accept. ## Consequences - One extra build step in `shared-types` (buf or protoc). - Breaking event changes cost something — good; they should. - ML pipelines can replay old events against new code with confidence. ## Non-consequences - No gRPC. HTTP stays HTTP/JSON. Protobuf is only the wire format on the event bus.