main content
< Volver a blog sobre aplicaciones móviles

Versionado de APIs: contratos y deprecación

Versionado de APIs sin romper nada: contratos y deprecación gradual en tu app a medida

Te lo digo desde la trinchera: el día que cambias un campo de una API sin avisar y un cliente con la app móvil sin actualizar empieza a recibir errores 500, no hay café que arregle la mañana. Lo he visto demasiadas veces. Un rename inocente de un atributo en el JSON, un tipo que pasa de string a número, un endpoint que «ya nadie usa» y que resulta que sí usaba el módulo de facturación. Y el problema casi nunca es técnico de verdad: es de proceso. Nadie versionó, nadie avisó, nadie tenía un contrato.

Así que vamos a lo concreto. Cómo montar un versionado de APIs serio, con contratos y deprecación gradual, para que puedas evolucionar tu backend sin dejar tirados a quienes consumen tu API.

Por qué el versionado no es opcional (aunque tu jefe diga que sí)

La regla mental que uso es simple: una vez que algo consume tu API, ese contrato deja de ser tuyo. Es compartido. Y los contratos compartidos no se cambian de forma unilateral sin consecuencias.

En una app a medida esto se vuelve crítico porque casi siempre tienes consumidores que no controlas del todo: una app móvil que el usuario tarda semanas en actualizar, una integración de un tercero, un panel interno legacy que nadie quiere tocar. Si rompes la compatibilidad, no rompes «el código»: rompes la confianza y, normalmente, la facturación de alguien.

Antes de versionar, ten clara una distinción que evita el 80% de los dramas: hay cambios compatibles hacia atrás y cambios rupturistas (breaking changes).

  • Compatible: añadir un campo opcional, añadir un endpoint nuevo, aceptar un parámetro nuevo opcional. El consumidor antiguo sigue funcionando igual.
  • Rupturista: eliminar o renombrar un campo, cambiar su tipo, hacer obligatorio algo que era opcional, cambiar códigos de estado o la semántica de una respuesta.

Si interiorizas esto, ya tienes media batalla ganada: los cambios compatibles no necesitan versión nueva; los rupturistas sí, siempre.

Estrategias de versionado: elige una y sé consistente

Hay tres enfoques principales y, sinceramente, importa más ser consistente que elegir «el mejor».

Versionado por URI

El clásico: /api/v1/pedidos, /api/v2/pedidos. Es feo para los puristas REST, pero es explícito, cacheable y trivial de depurar. Cuando un cliente te pega un log, ves la versión a simple vista. Para la mayoría de apps a medida, es lo que recomiendo por defecto. Lo que ves es lo que hay.

Versionado por cabecera

Mandas la versión en un header, por ejemplo Accept: application/vnd.miapp.v2+json o un X-API-Version: 2. Es más «limpio» en cuanto a la URL y permite negociación de contenido, pero tiene una trampa: es invisible. Se cachea peor, se depura peor y muchos clientes se olvidan de mandarlo. Si lo usas, define un comportamiento por defecto claro cuando falte la cabecera (y no, «la última» casi nunca es buena idea por defecto; usa la más antigua estable).

Versionado semántico (semver) del contrato

Aquí no hablamos de la URL, sino de cómo numeras el contrato: MAJOR.MINOR.PATCH. Subes MAJOR cuando hay un cambio rupturista, MINOR cuando añades algo compatible y PATCH para correcciones. La clave operativa: solo el cambio de MAJOR exige una versión nueva de API expuesta (una v2 en la URI, por ejemplo). Minor y patch viajan dentro de la misma versión mayor sin romper a nadie.

Mi recomendación pragmática: URI para la versión mayor visible + semver para gobernar el contrato por detrás. Lo mejor de los dos mundos sin volverte loco.

Los contratos: tu única defensa real contra el caos

Versionar sin contrato es ponerle número de serie al caos. El contrato es lo que define, de forma verificable, qué promete tu API. Y «verificable» es la palabra clave: si no se puede testear automáticamente, no es un contrato, es un PDF que nadie lee.

OpenAPI como fuente de verdad

Escribe la especificación OpenAPI (antes Swagger) y trátala como código: en el repo, revisada en pull request, con su diff visible. A partir de ahí ganas tres cosas: documentación siempre actualizada, generación de clientes y stubs, y —lo importante— validación automática de que tu implementación cumple lo que promete.

El truco que más dolor me ha ahorrado: meter en el pipeline de CI un linter de diff de OpenAPI que detecte breaking changes automáticamente. Si alguien borra un campo o cambia un tipo en la v1, el build falla. Punto. No depende de que un humano se acuerde en la revisión a las ocho de la tarde.

Consumer-driven contracts

Para integraciones serias entre servicios, da un paso más con contratos dirigidos por el consumidor (consumer-driven contracts, con herramientas tipo Pact). La idea es potente: cada consumidor declara qué espera exactamente de tu API, y tú ejecutas esos contratos contra tu implementación en CI.

¿El resultado? Antes de desplegar, sabes a quién vas a romper. No te enteras en producción con un cliente gritando; te enteras en el pipeline, con tiempo para reaccionar. En arquitecturas con varios equipos o microservicios, esto vale su peso en oro.

Deprecación gradual: cómo retirar una versión sin víctimas

Crear una v2 es la parte fácil. La difícil es jubilar la v1 sin dejar cadáveres. Y aquí la regla número uno es: nunca apagues una versión de golpe. La deprecación es un proceso con fases, no un interruptor.

El ciclo de vida de una versión

Yo lo divido en cuatro estados claros:

  1. Activa: la versión recomendada, soporte completo.
  2. Deprecada: sigue funcionando, pero está marcada como «en retirada». Aquí empieza el reloj. Nada se rompe todavía.
  3. Sunset: tiene fecha de apagado pública y comunicada. El que no haya migrado, que corra.
  4. Retirada: devuelve 410 Gone. Se acabó.

El error clásico es saltar de «activa» a «retirada» sin pasar por el medio. No lo hagas.

Sunset headers: avisa en el propio protocolo

Aquí hay algo que mucha gente desconoce y que es oro: existen cabeceras HTTP estándar para anunciar la retirada dentro de las propias respuestas.

  • Deprecation: true (o con una fecha) marca un endpoint como deprecado.
  • Sunset: Sat, 31 Dec 2026 23:59:59 GMT anuncia la fecha exacta de apagado.
  • Acompáñalo de un Link apuntando a la documentación de migración.

La gracia es que el aviso viaja en cada respuesta, así que un equipo atento puede detectarlo automáticamente monitorizando sus llamadas. No dependes solo de que alguien lea un email.

Comunicar a los consumidores (la parte que casi todos olvidan)

Las cabeceras están muy bien, pero no sustituyen el aviso humano. Una deprecación bien hecha combina varias capas:

  • Aviso explícito y con antelación generosa: para apps móviles, piensa en meses, no semanas. La gente no actualiza.
  • Métricas de uso por versión: si no sabes cuántas peticiones recibe aún tu v1 y de quién, vas a ciegas. Instrumenta esto desde el día uno. Es la diferencia entre apagar con datos o apagar a fe.
  • Logs de cliente deprecado: registra qué consumidores siguen usando la versión vieja para poder ir a buscarlos uno a uno si hace falta.
  • Documentación de migración clara: qué cambia, cómo se traduce cada campo, ejemplos antes/después.

Y una recomendación final que da mucha tranquilidad: mantén al menos dos versiones mayores conviviendo. Mientras la v2 es la activa, la v1 sigue deprecada pero viva. Eso da margen real a tus consumidores para migrar sin agobios ni incidencias a medianoche.

Júntalo todo: un proceso, no un truco

Si tuviera que resumirlo en una receta: define el contrato en OpenAPI, métele validación de breaking changes en CI, versiona la mayor en la URI, gobierna el detalle con semver, mide el uso por versión y deprecá con fases claras y sunset headers. No es glamuroso, pero es lo que separa una API en la que se confía de una que da sustos cada despliegue.

Lo he montado las veces suficientes como para saber que el coste de hacerlo bien desde el principio es ridículo comparado con el de arreglarlo después, con clientes enfadados de por medio. Si estás construyendo o evolucionando una app a medida y quieres montar este versionado bien desde la base, en Tangram podemos ayudarte a diseñarlo: hablemos de tu caso sin compromiso.

Versionar bien no va de tecnología puntera. Va de respetar el contrato que tienes con quien depende de ti. Hazlo con disciplina y dejarás de temer los despliegues.

Contacta con nosotros
Fila 1