El desplazamiento de esquemas es una de las formas más silenciosas de romper un pipeline en producción. Un equipo productor añade un campo, elimina uno que consideraba sin uso o renombra un tipo en su modelo interno. El cambio supera la revisión de código, se despliega sin errores y las métricas del productor lucen perfectas. Mientras tanto, un consumidor en el otro extremo empieza a lanzar errores de deserialización o, peor aún, descarta valores en silencio y sigue funcionando. Para cuando alguien lo detecta, el daño ya está en agregaciones downstream, dashboards o registros de auditoría.
Este artículo cubre la mecánica de la evolución de esquemas en event streaming de forma sistemática: cómo un schema registry aplica la compatibilidad de esquemas en Kafka, qué modo de compatibilidad encaja con cada patrón operativo, cómo el orden de actualización interactúa con esos modos, y cómo se comportan Avro, Protobuf y JSON Schema bajo las mismas reglas. La gestión del contrato entre productor y consumidor no es un lujo; es la disciplina que separa un pipeline en tiempo real de una bomba de relojería.
Por qué Kafka necesita una capa de contrato explícita
Kafka es agnóstico al formato. Transfiere bytes y no valida qué significan esos bytes. Productores y consumidores deben acordar un tipo de dato común de forma independiente, y en un sistema donde los servicios se despliegan en calendarios distintos, ese acuerdo se degrada con el tiempo si nada lo aplica.
Un schema registry es esa capa de aplicación. Se sitúa entre productores y consumidores, mantiene un historial versionado de cada esquema y verifica la compatibilidad antes de aceptar una nueva versión. Cuando un productor publica un mensaje, embebe el ID del esquema al inicio del payload. El consumidor lee ese ID, obtiene el esquema correspondiente del registry (almacenándolo en caché localmente) y deserializa en consecuencia. El registry no se limita a almacenar esquemas; es el mecanismo que hace seguras las actualizaciones progresivas.
Los siete modos de compatibilidad y lo que realmente significan
El Schema Registry de Confluent define siete niveles de compatibilidad válidos: BACKWARD, BACKWARD_TRANSITIVE, FORWARD, FORWARD_TRANSITIVE, FULL, FULL_TRANSITIVE y NONE. La mayoría de los equipos solo piensa en tres, y así terminan con problemas causados por los otros cuatro.
Backward (el valor predeterminado)
BACKWARD es el modo predeterminado del Schema Registry y el punto de partida más habitual. Una nueva versión de esquema es compatible con los datos escritos por la versión anterior. En la práctica, puedes eliminar campos y añadir campos, siempre que los nuevos lleven un valor por defecto. La regla operativa clave: actualiza los consumidores antes que los productores. El nuevo consumidor debe poder leer mensajes antiguos antes de que el productor empiece a escribir con el nuevo esquema.
La razón principal por la que BACKWARD es el modo predeterminado en Kafka es que permite rebobinar los consumidores al inicio de un topic y reproducir datos históricos con un esquema nuevo. Esa propiedad es innegociable en pipelines que necesitan replay para recuperación o backfill.
Forward
FORWARD invierte la relación: los consumidores antiguos pueden leer datos escritos por un productor nuevo. Puedes eliminar campos que tienen valores por defecto y añadir campos nuevos, pero no tienes garantizada la capacidad de leer mensajes antiguos con un esquema nuevo. Actualiza primero los productores. La compatibilidad forward es más difícil de mantener porque exige anticipar todos los cambios futuros en el momento de diseñar el esquema. En la práctica, los equipos que la usan tienden a tener formas de mensaje estables y bien comprendidas.
Full
FULL exige que un esquema sea compatible tanto hacia atrás como hacia adelante de forma simultánea. Los únicos cambios permitidos son añadir o eliminar campos opcionales, es decir, campos que llevan valores por defecto en ambas direcciones. La ventaja es que puedes actualizar productores y consumidores en cualquier orden, lo que elimina por completo el coste de coordinación. Para equipos que ejecutan muchos servicios independientes contra un topic compartido, la compatibilidad full suele valer la restricción.
None
NONE desactiva toda verificación de compatibilidad. El registry acepta cualquier versión de esquema independientemente de lo que haya cambiado. No es un atajo; la carga de coordinación recae íntegramente en los equipos implicados y, en la mayoría de los casos, requiere actualizar productores y consumidores de forma simultánea. Úsalo solo cuando dispongas de herramientas de orquestación explícitas y una razón clara.
Variantes transitivas: la dimensión que se pasa por alto
La compatibilidad no transitiva solo verifica contra la última versión del esquema. La compatibilidad transitiva verifica contra todas las versiones históricas. La diferencia es invisible hasta que has acumulado suficientes cambios pequeños e individualmente seguros como para romper a un consumidor que se saltó varias versiones.
Los modos no transitivos (BACKWARD, FORWARD, FULL) validan un nuevo esquema solo contra la versión inmediatamente anterior. Las variantes transitivas verifican contra todas las versiones históricas. En sistemas de larga vida donde los consumidores pueden estar varias versiones por detrás, los modos transitivos evitan la acumulación de cambios que son individualmente seguros pero colectivamente incompatibles. Una nota práctica: si dependes de un valor por defecto en modo transitivo, ese valor debe haberse declarado desde la primera versión del esquema.
Una restricción adicional que vale la pena mencionar: Kafka Streams solo admite compatibilidad BACKWARD. Si tu topología usa Kafka Streams para procesamiento con estado, esa decisión ya está tomada por ti.
Comportamiento específico por formato bajo las mismas reglas
Los modos de compatibilidad son agnósticos al formato a nivel del registry, pero el formato de serialización determina qué cambios son físicamente posibles dentro de cada modo.
Avro
Avro es la opción más habitual para la flexibilidad en la evolución de esquemas. Los campos nuevos pueden añadirse de forma compatible hacia atrás siempre que lleven un valor por defecto. Los campos se hacen opcionales usando un tipo union que incluye null. Avro no requiere generación de código, lo que reduce la fricción en equipos que iteran frecuentemente sobre esquemas. Es la opción correcta cuando los esquemas cambian con frecuencia y necesitas que el registry haga el trabajo pesado.
Protobuf
El modelo de evolución de Protobuf se basa en números de campo en lugar de nombres de campo. Los números de campo deben mantenerse estables; los campos eliminados deben marcarse con la palabra clave reserved para evitar la reutilización de su tag. Los campos desconocidos se ignoran durante la deserialización, lo que hace seguro para los consumidores antiguos añadir nuevos campos opcionales. Protobuf genera payloads más pequeños y tiene buen rendimiento entre lenguajes, pero requiere generación de código y mayor disciplina en la gestión de números de campo.
JSON Schema
JSON Schema es el formato más legible pero el más frágil bajo la evolución. El problema principal es el modelo de contenido abierto: un esquema que permite propiedades adicionales tratará la definición de una nueva propiedad como un cambio incompatible hacia atrás si los datos antiguos contienen una propiedad del mismo nombre con un tipo diferente. JSON Schema es adecuado cuando la legibilidad humana importa más que la eficiencia, o cuando se integra con APIs REST que ya usan JSON, pero no es una buena opción para pipelines de alto rendimiento que evolucionan con frecuencia.
Estrategias de nombrado de subjects y topics con múltiples tipos
El Schema Registry organiza los esquemas por subject, y la estrategia de nombrado determina cómo los subjects se mapean a los topics. La predeterminada es TopicNameStrategy: el nombre del subject es el nombre del topic con el sufijo -key o -value. Funciona bien cuando un topic transporta un único tipo de mensaje.
Cuando un topic transporta múltiples tipos de evento, TopicNameStrategy falla porque todos los mensajes del topic comparten un mismo subject de esquema. RecordNameStrategy usa el nombre de registro completamente calificado como subject, desacoplando el versionado del esquema de la estructura del topic. TopicRecordNameStrategy combina ambos, limitando el nombre del registro a un topic específico. Elegir la estrategia correcta desde el principio forma parte de las buenas prácticas del schema registry; adaptarla después requiere una migración coordinada.
Funcionamiento en producción
Usamos NATS JetStream como backbone interno de event streaming y operamos infraestructura compatible con Kafka para clientes bajo los mismos estándares operativos que rigen el resto de nuestra plataforma: instalaciones Tier III, certificación ISO 27001 y un SLA contractual de 99,9% de disponibilidad que es un compromiso escrito, no una promesa de marketing. La gobernanza de esquemas forma parte de la conversación de diseño de pipeline que mantenemos con los clientes antes de su incorporación, no una solución de emergencia cuando algo falla.
La disciplina práctica para cualquier equipo que gestiona la evolución de esquemas a escala se resume en una lista corta: elige un modo de compatibilidad que se ajuste a tu modelo de despliegue y mantenlo, usa variantes transitivas para cualquier topic con una ventana de retención larga, define valores por defecto para cada campo que puedas querer eliminar más adelante, y trata la regla de orden de actualización de tu modo elegido como una restricción de despliegue estricta, no como una recomendación. El desplazamiento de esquemas es prevenible; solo requiere tratar el contrato productor-consumidor con el mismo rigor que aplicas al versionado de APIs.




