Planteamiento y cuándo aplica
Una API REST pública de Orders ya es utilizada por 600 integraciones de terceros y varias aplicaciones móviles a las que no se les puede exigir una actualización inmediata. El GET /v1/orders existente devuelve todas las órdenes en una sola respuesta. Cada orden contiene una cadena customer_name, y status actualmente solo devuelve pending u paid. La próxima versión debe exponer los datos del cliente como un objeto estructurado, paginar la lista e introducir el estado refunded.
Las 600 integraciones, los campos actuales y los valores de estado son supuestos de la entrevista. La restricción vinculante es que el proveedor no puede controlar cuándo se actualiza cada consumidor. El éxito abarca una v2 funcional, la continuidad del comportamiento de la v1 bajo el contrato original, una migración observable, fechas de obsolescencia detectables y la recuperación ante un despliegue fallido sin alterar el significado prometido de una versión.
Esta es una pregunta de backend porque evalúa contratos de API, representaciones del lado del servidor, ingeniería de releases y gobierno de compatibilidad. No requiere diseñar el sistema completo de órdenes ni un plan de sharding de base de datos. Escribe el contrato existente antes de clasificar cada cambio. Limitarse a decir "pon v2 en la URL" no demuestra que los clientes antiguos permanezcan a salvo.
{
"orders": [
{
"id": "ord_1",
"customer_name": "Ada Lovelace",
"status": "paid"
}
]
}Qué evalúan los entrevistadores
La primera señal es si el candidato distingue tres tipos de compatibilidad. La compatibilidad a nivel de código fuente (source compatibility) evalúa si el código del cliente antiguo sigue compilando tras regenerar o actualizar su SDK. La compatibilidad a nivel de protocolo (wire compatibility) evalúa si un serializador antiguo puede procesar el nuevo mensaje. La compatibilidad semántica (semantic compatibility) evalúa si la misma llamada sigue comportándose como un consumidor razonable esperaría. Un tipo de campo sin cambios no preserva la semántica por sí solo. Cambiar silenciosamente "devolver todas las órdenes" por "devolver las primeras 100 órdenes" sigue produciendo JSON válido, pero los clientes antiguos pierden datos.
La segunda señal es el criterio basado en contratos. Una tabla universal memorizada no puede representar el comportamiento de procesamiento de cada cliente real. Un nuevo campo de solicitud opcional cuya omisión preserve el comportamiento anterior generalmente puede permanecer en la v1. Eliminar, renombrar o cambiar el tipo de un campo rompe la v1. Un campo de respuesta adicional es una adición segura solo si el contrato permite campos desconocidos y los SDK reales los ignoran. Agregar un valor a un enum de respuesta amerita un análisis más riguroso: un contrato con enum abierto puede permitir la expansión, mientras que un enum cerrado, un SDK fuertemente tipado generado o un switch exhaustivo sin rama por defecto pueden fallar.
La tercera señal es una ruta ejecutable que conecte versiones, implementación y ciclo de vida. Una respuesta sólida preserva la capa de presentación de la v1, utiliza lógica de dominio compartida para producir respuestas separadas de v1 y v2, ejecuta pruebas de diff de contrato y de SDK antiguos antes de la publicación, mide la adopción, los errores y la latencia por consumidor después, y finalmente retira la versión antigua con señales de obsolescencia estandarizadas, una guía de migración y puertas de apagado (shutdown gates) explícitas. Un identificador de versión es una decisión de enrutamiento; no realiza ninguna de esas tareas automáticamente.
Preguntas para aclarar antes de responder
- ¿Se pueden controlar los consumidores? Tres servicios dentro de una misma empresa que pueden coordinar un despliegue atómico pueden utilizar expand–migrate–contract sin una v2 de larga duración. Los terceros y los clientes móviles antiguos requieren un límite de versión estable y un ciclo de vida público.
- ¿El contrato actual exige que los clientes ignoren campos y valores de enum desconocidos? Esto determina si un campo de respuesta o un valor de enum añadido puede permanecer en la v1. Un JSON válido por sí solo es insuficiente; inspecciona el contrato OpenAPI, los tipos de SDK y el comportamiento real de los consumidores.
- ¿Qué tan grande es la lista y qué riesgo de confiabilidad genera? Si devolver todas las órdenes aún cumple con el SLO, la paginación puede existir únicamente en la v2. Si una respuesta no delimitada ya amenaza la disponibilidad, pueden ser necesarios límites de tasa (rate limits) y una vía de comunicación de emergencia, pero un truncamiento silencioso sigue sin ser un cambio compatible.
- ¿La API ya ha publicado un mecanismo de selección de versión? Continúa utilizando
/v1y/v2cuando ya existan versiones en la ruta, o mantén el encabezado de fecha existente cuando las versiones se basen en encabezados. Cambiar el mecanismo durante la migración genera otro cambio para los clientes. - ¿Puede el proveedor identificar a cada consumidor y contactar a su propietario? Los IDs de aplicación estables, las versiones de SDK y los contactos de los propietarios permiten un seguimiento preciso de la migración. El tráfico anónimo requiere compuertas de retiro más conservadoras.
- ¿Qué período de soporte legal, contractual o comercial se prometió? La fecha de apagado proviene de la política publicada, las obligaciones con los clientes, el riesgo y la adopción real. El período de soporte de otra plataforma no es una regla universal.
Marco de respuesta de 30 segundos
"Primero congelaría el contrato escrito y el comportamiento observable de la v1, y luego clasificaría cada propuesta en compatibilidad de código fuente, de protocolo y semántica. Un nuevo campo opcional con un comportamiento predeterminado sin cambios puede encajar en la v1. Reemplazar una cadena por un objeto, renombrar un campo y cambiar una lista de resultados totales por paginación requieren v2. Un nuevo valor de enum en la respuesta depende de la política de enums abiertos y del comportamiento de los SDK antiguos. Compartiría la lógica de dominio de Orders y mantendría únicamente adaptadores de representación para v1 y v2. Antes de la publicación, ejecutaría un diff de OpenAPI, pruebas con SDK antiguos, reproducción de solicitudes registradas y pruebas de contrato de extremo a extremo; luego permitiría que los consumidores conocidos adopten la v2 de forma opt-in. Monitorearía la adopción y los errores por consumidor, publicaría las fechas de migración, obsolescencia y apagado, y retiraría la v1 solo después de superar las compuertas de migración y obligaciones contractuales. Ante cualquier falla, puedo revertir la ruta o el adaptador de la v2 mientras la v1 permanece sin cambios".
Análisis detallado paso a paso
Comienza construyendo una línea base de compatibilidad. Conserva el documento OpenAPI actual, los SDK publicados, solicitudes y respuestas representativas, códigos de error, ordenamiento, valores predeterminados y el comportamiento de las listas. Muestrea también el comportamiento visible pero no documentado, ya que los consumidores pueden depender de formatos de campo, manejo de nulos, ordenamiento o de obtener el conjunto de resultados completo en una sola respuesta. Registra al mismo tiempo el ID del consumidor, la versión, el volumen de solicitudes y el propietario. Más adelante, esto separará lo "no utilizado" de lo "utilizado por alguien a quien el proveedor no puede identificar".
Luego, clasifica cada cambio propuesto:
| Cambio propuesto | Evaluación en v1 | Tratamiento |
|---|---|---|
| Agregar un campo de solicitud opcional cuya omisión preserve el comportamiento anterior | Usualmente compatible | Agregar a v1 y probar solicitudes antiguas |
| Agregar un campo de respuesta opcional | Condicionalmente compatible | Verificar primero la política de campos desconocidos y los SDK antiguos |
Reemplazar customer_name con un objeto customer | Incompatible | Mantener la cadena en v1; devolver el objeto en v2 |
| Eliminar o renombrar un campo existente | Incompatible | Agregar el nuevo nombre en una nueva versión; no eliminarlo de v1 |
| Cambiar una lista de todos los resultados por paginación por defecto | Semánticamente incompatible | Definir la semántica de cursores y páginas en v2 |
Agregar refunded a un enum de respuesta | Depende del contrato del enum | Inspeccionar reglas de enums abiertos, código generado y switches exhaustivos |
| Corregir errores ortográficos no documentados que no afecten dependencias razonables | Aún requiere evidencia | Demostrarlo con pruebas de consumidores y reproducción de tráfico |
La paginación es el caso más fácil de subestimar. La guía de compatibilidad de Google advierte sobre el riesgo de agregar un page_size predeterminado finito a una API que antes devolvía todos los elementos: un cliente antiguo puede asumir erróneamente que la primera respuesta está completa. Define items, next_page_token, el ordenamiento y las reglas de invalidez de tokens en v2. Durante el período de soporte, v1 mantiene su semántica original mientras que las cuotas, el monitoreo del tamaño de respuesta y el contacto de migración controlan el riesgo operativo.
A continuación, traza el límite de la versión. El planteamiento ya utiliza control de versiones por ruta, así que añade /v2/orders. No adivines la versión a partir del User-Agent en la misma ruta y no enrutes v1 silenciosamente hacia una representación con nueva semántica. Versiona solo la representación externa. Procesa cada solicitud hacia el mismo comando de dominio, comparte la lógica de consulta de órdenes y autorización, y luego utiliza V1OrderPresenter u V2OrderPresenter para producir la estructura correspondiente. Las correcciones de seguridad y las reglas de negocio pueden llegar a ambas versiones sin duplicar el servicio.
Una respuesta ilustrativa de v2 es:
{
"orders": [
{
"id": "ord_1",
"customer": {
"display_name": "Ada Lovelace"
},
"status": "paid"
}
],
"next_page_token": "eyJvcmRlcl9pZCI6Im9yZF8xIn0"
}Utiliza cuatro compuertas de release. Primero, compara las definiciones OpenAPI antiguas y nuevas y rechaza la eliminación de campos en v1, cambios de obligatoriedad, cambios de tipo y nuevas reglas de validación accidentales. Segundo, compila y ejecuta casos de contrato fijos con el último SDK público de v1, incluyendo campos desconocidos, nulos, respuestas de error y enums. Tercero, reproduce solicitudes sanitizadas representativas y compara códigos de estado, campos críticos y ordenamiento entre las implementaciones antigua y nueva de v1. Cuarto, permite que un pequeño conjunto de consumidores conocidos adopte la v2 en un entorno de sandbox o canary y observa la funcionalidad, 4xx, 5xx, latencia y tamaño de respuesta. Un diff de esquema puede detectar cambios estructurales; no puede reemplazar las aserciones semánticas sobre ordenamiento, valores predeterminados o completitud de páginas.
La migración comienza como opt-in. Publica la documentación de v2, SDKs, una tabla de migración campo por campo y un sandbox compatible con ambas versiones. Muestra a cada consumidor conocido su volumen de llamadas a v1, endpoints con fallas y fecha objetivo. Migra primero los ejemplos del proveedor y los SDK oficiales para que las brechas en la guía salgan a la superficie temprano. Revisa la adopción por consumidor e inspecciona las solicitudes agregadas por separado: una integración de conciliación de fin de mes de bajo volumen puede ser más crítica que muchos health checks. Rastrea consumidores activos únicos por versión, volumen de solicitudes, 4xx, 5xx, latencia p95, completitud de paginación, fallas de parseo en SDKs antiguos y cuentas de alto riesgo contactadas que no han migrado.
Separa tres momentos del ciclo de vida: publicar la política de migración, declarar formalmente la obsolescencia (deprecation) de la API y hacer que deje de responder. RFC 9745 define el encabezado de respuesta Deprecation para una fecha de obsolescencia y la relación de enlace deprecation para información de soporte. Agrega Sunset solo cuando el proveedor planee que el recurso deje de responder. La obsolescencia en sí no debe cambiar el comportamiento del recurso. Estas fechas son ejemplos de entrevista; las fechas reales deben seguir la política publicada:
Deprecation: @1803859200
Sunset: Wed, 01 Sep 2027 00:00:00 GMT
Link: <https://api.example.com/migrations/orders-v2>; rel="deprecation"; type="text/html"Antes de retirar la v1, exige que se cumpla todo lo siguiente: las obligaciones de soporte están satisfechas; los consumidores críticos conocidos han migrado o cuentan con una excepción aprobada; el tráfico restante está explicado; la guía de migración y el canal de soporte funcionan; se superan las compuertas de errores, latencia y resultados comerciales de v2; y un ensayo de apagado puede revertirse. Si un cliente de alto valor sigue bloqueado, extiende el soporte, proporciona un gateway de compatibilidad restringido o cumple el contrato. No ignores a ese cliente solo para mostrar un 100% de adopción.
Define el plan de reversión (rollback) antes del lanzamiento. La ruta de v2 y el adaptador de presentación pueden desactivarse de forma independiente, las escrituras de dominio siguen siendo retrocompatibles y la v1 conserva su último artefacto verificado. Si un campo de v2 necesita un nuevo almacenamiento, amplía y rellena (backfill) ese almacenamiento antes de que v2 lo lea; no elimines datos requeridos por v1 en el release de v2. Revertir v2 significa restaurar su implementación. Cambiar lo que significa "v2" para ocultar el incidente violaría nuevamente el contrato.
Ejemplo de respuesta de alta calidad
"Separaría los cambios de contrato del ciclo de vida del release. A estos clientes no se les puede exigir una actualización inmediata, por lo que necesito preservar el parseo de JSON, la ejecución en SDKs antiguos y resultados completos con el mismo significado para la misma solicitud.
Cambiar customer_name de una cadena a un objeto altera su tipo, y renombrarlo es una operación de eliminación y adición, por lo que ambos pertenecen a v2. Paginar GET /orders por defecto provocaría que los clientes antiguos pierdan resultados, lo que representa una ruptura semántica y también pertenece a v2. No asumiría que añadir refunded es seguro. Si el enum de respuesta está documentado como abierto y los SDK antiguos preservan valores desconocidos, v1 puede ampliarse. Si el código generado utiliza un enum cerrado o los consumidores hacen un switch exhaustivo sobre él, mantendría el nuevo valor en v2 o primero establecería y probaría una ruta segura para valores desconocidos.
Preservaría la respuesta y la semántica de todos los resultados de /v1/orders, y luego crearía /v2/orders con un objeto customer estructurado, cursor y reglas de página. Ambas versiones comparten la lógica de consultas, autorización y estado de las órdenes; solo difieren el procesamiento de solicitudes y la presentación de respuestas. Antes de fusionar (merge), compararía definiciones OpenAPI, ejecutaría pruebas de contrato a través del último SDK de v1 y reproduciría solicitudes sanitizadas para verificar códigos de estado, ordenamiento, valores predeterminados y completitud. Los SDK internos y un pequeño conjunto de integraciones conocidas adoptan v2 primero. Una regresión desactiva la ruta de v2 mientras v1 continúa sin cambios.
Durante la migración, mediría los consumidores activos por ID de aplicación, utilizaría el tráfico total como señal de apoyo y rastrearía la adopción de versiones, fallas de parseo, 4xx, 5xx, latencia, completitud de paginación y cuentas críticas. La guía incluye mapeos de campos, el bucle de paginación, alternativas para enums y un entorno de prueba. La obsolescencia formal es detectable mediante encabezados de respuesta y un enlace de migración; el apagado tiene una fecha declarada por separado. Retiro v1 solo después de que las obligaciones de soporte, los consumidores críticos, el tráfico restante y los SLO de v2 superen todas sus compuertas, y tras un ensayo reversible. Eso convierte al identificador de versión, la implementación compatible, la evidencia de migración y el retiro en un único plan verificable".
Errores comunes
- Responder únicamente "pon
/v2en la URL" → No identifica a quién afecta la rotura ni las compuertas de migración y apagado → Establece una línea base de v1 y clasifica cada cambio según la compatibilidad de código fuente, de protocolo y semántica. - Asumir que toda adición a la respuesta es compatible → Los deserializadores estrictos, los enums cerrados y los switches exhaustivos aún pueden fallar → Inspecciona el contrato público y los SDK generados; luego ejecuta pruebas reales sobre versiones antiguas.
- Redirigir automáticamente v1 a v2 → Un solo identificador de versión pasa a representar dos semánticas distintas, impidiendo que los clientes elijan o reviertan → Conserva una representación estable de v1 y exige la selección explícita de v2.
- Copiar el servicio completo para v1 y v2 → Las correcciones de seguridad y las reglas de negocio divergen mientras crece el costo de mantener dos versiones → Comparte la lógica de dominio y aísla solo el procesamiento y la presentación que realmente difieren.
- Apagar v1 tan pronto llega la fecha anunciada → Llamadas anónimas de cola larga, conciliaciones de baja frecuencia y clientes críticos aún pueden depender de ella → Verifica la adopción por consumidor, las obligaciones y el tráfico restante; luego ensaya la recuperación.
- Observar únicamente las tasas de 2xx del lado del servidor → Un cliente puede recibir una respuesta pero fallar al parsearla, omitir páginas o malinterpretar un nuevo estado → Agrega resultados de SDKs antiguos, completitud de paginación, resultados de extremo a extremo y señales de soporte.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Tres consumidores internos que pertenecen a la misma empresa necesitan v2?
No necesariamente. Si cada cliente es identificable, los lanzamientos pueden coordinarse y la reversión es rápida, se puede usar expand–migrate–contract: agregar un campo o endpoint compatible, lanzar consumidores capaces de leer ambas estructuras, cambiar el productor y luego eliminar el contrato anterior cuando el uso medido llegue a cero. Las pruebas de contrato y la evidencia de despliegue versionado siguen siendo importantes, pero los consumidores controlables no requieren una v2 pública permanente. Un proceso batch no coordinado, un cliente antiguo o un socio externo invalidan esta premisa.
Pregunta de seguimiento 2: ¿Los eventos de webhook deben seguir la versión de API actual de la cuenta?
No reinterpretes eventos históricos con la versión "actual" durante una reproducción. Fija una versión de la API del evento al crear el endpoint, regístrala junto con el evento y preserva la estructura original para reintentos y reproducciones. Actualiza creando o cambiando a un endpoint con la nueva versión y validando al consumidor. La documentación pública de Stripe vincula igualmente la estructura del evento webhook a la versión de API al momento de crear el endpoint. Enviar eventos tanto antiguos como nuevos genera efectos secundarios duplicados y solo es seguro como una migración breve cuando los consumidores desduplican mediante un ID de evento estable.
Pregunta de seguimiento 3: ¿Un nuevo valor de enum en la respuesta es un cambio disruptivo (breaking change)?
Depende del contrato publicado. GitHub considera que agregar un valor de enum es una adición no disruptiva, mientras que la guía de compatibilidad de Google advierte que el código antiguo puede no procesar adecuadamente un nuevo valor de enum en la respuesta. Expón esa tensión explícitamente. Si el contrato define un conjunto abierto, el SDK expone una representación para valores desconocidos y los consumidores deben tolerarla, el cambio puede ser compatible. Si el tipo es cerrado o el ecosistema contiene switches exhaustivos, trátalo como un riesgo disruptivo: mejora primero el SDK y el contrato o introduce el valor en una nueva versión. Un esquema del lado del servidor no puede decidir esto por sí solo.
Pregunta de seguimiento 4: La lista no delimitada de v1 está sufriendo timeouts. ¿Qué ocurre si la migración no puede finalizar a tiempo?
Primero restablece el servicio con los controles permitidos por el contrato existente: cuotas, almacenamiento en caché, optimización de consultas y backpressure, mientras migras directamente a los consumidores de alto volumen a la v2. Si un límite de respuesta de emergencia es inevitable, declara explícitamente que puede romper la v1, utiliza el proceso de incidentes y aprobación de cambios, anuncia el alcance afectado, proporciona exportación masiva o un canal de compatibilidad temporal, y monitorea el riesgo de pérdida de datos. Devolver silenciosamente los primeros 100 registros con una respuesta 200 convierte un incidente de disponibilidad en un error de datos difícil de detectar; no es una solución compatible.
Pregunta de seguimiento 5: Llega la fecha de apagado, pero el 0.2% del tráfico aún utiliza v1. ¿Qué hacer a continuación?
Desglosa el porcentaje en consumidores y propósito de negocio: un health check de monitoreo, una mala configuración, un job de fin de mes o un cliente con contrato. Elimina el tráfico identificable que no tenga dependencia comercial. Los consumidores críticos necesitan una actualización, una excepción o escalación de soporte. Gestiona el tráfico anónimo según la política publicada y el modelo de riesgo. Registra las llamadas restantes, evidencia de notificaciones, obligaciones de soporte, el plan de recuperación y el responsable de la decisión. Un porcentaje por sí solo no demuestra que el apagado sea seguro ni que la versión deba mantenerse para siempre.