StackPractices
intermediate Por Mathias Paulenko

Plantilla del Ciclo de Vida de APIs

Una plantilla de checklist lista para copiar para gestionar la deprecación de APIs, las transiciones de versionado y los cierres seguros.

Visión General

Las APIs son contratos de larga duración entre sistemas. Cambiar o eliminar un endpoint sin un proceso estructurado rompe consumidores downstream, causa caídas y quema una confianza que tarda trimestres en recuperarse. Esta plantilla te da un checklist para los tres momentos de riesgo en la vida de una API: deprecar una versión antigua, lanzar una nueva y apagar una API definitivamente.

El ciclo de vida solo avanza en una dirección. Toda versión acaba retirada — la única pregunta abierta es si los consumidores estaban listos cuando ocurrió:

flowchart diagram: Dev[

Documentos relacionados: Diseñar un API Gateway Escalable para Microservicios, Construir Sistemas Resilientes con el Circuit Breaker y Inyección de Dependencias.

Cuándo Usar

Usa este recurso cuando:

  • Planeas deprecar un endpoint o versión de API
  • Introduces un cambio incompatible que requiere una nueva versión
  • Preparas el cierre de toda una API o servicio
  • Auditas si una deprecación en curso va realmente por buen camino

No lo necesitas cuando el cambio es puramente aditivo — un campo opcional o endpoint nuevo sale en la versión actual con una entrada de changelog, no con un plan de deprecación. Para alternativas, consulta la Plantilla de Changelog de API.

Solución

# Gestión del Ciclo de Vida de API: `<Nombre de la API>`

## 1. Metadatos de la API

| Campo | Valor |
|-------|-------|
| Nombre de API | `nombre` |
| Versión Actual | `v2.3` |
| URL Base | `https://api.example.com/v2` |
| Equipo Responsable | `@platform-team` |
| Consumidores | Internos: 3, Externos: 12 |
| Estado del Ciclo de Vida | Publicada / Deprecada / Sunset / Retirada |

## 2. Checklist de Deprecación

### 2.1. Decisión y Comunicación

- [ ] Documentar la razón de la deprecación (seguridad, rendimiento, mantenibilidad)
- [ ] Identificar todos los consumidores del endpoint/versión deprecado
- [ ] Establecer fecha de deprecación (mínimo 6 meses para APIs externas, 3 meses para internas)
- [ ] Publicar aviso de deprecación en:
  - [ ] Documentación de la API (changelog)
  - [ ] Portal de desarrolladores / página de estado
  - [ ] Email directo a consumidores registrados
  - [ ] Headers de respuesta (`Deprecation`, `Sunset`, relaciones `Link`)

### 2.2. Ruta de Migración

- [ ] Proporcionar guía de migración con ejemplos antes/después
- [ ] Ofrecer entorno sandbox para probar la nueva versión
- [ ] Programar sesiones de preguntas para equipos consumidores
- [ ] Crear shim de compatibilidad si la migración es compleja

### 2.3. Monitoreo

- [ ] Rastrear tráfico al endpoint deprecado diariamente
- [ ] Alertar cuando el uso baje del umbral (listo para cierre)
- [ ] Mantener dashboard de progreso de migración de consumidores
- [ ] Registrar qué consumidores siguen llamando a la versión deprecada, no solo cuántos

## 3. Checklist de Versionado

### 3.1. Selección de Versión

- [ ] Determinar si el cambio es compatible (patch/minor) o incompatible (major)
- [ ] Seguir versionado semántico: `MAJOR.MINOR.PATCH`
- [ ] Actualizar ruta URL (`/v3/`) o usar versionado por headers (`Accept: application/vnd.api.v3+json`)

### 3.2. Release

- [ ] Desplegar la nueva versión junto a la antigua
- [ ] Actualizar documentación con nuevos ejemplos de request/response
- [ ] Ejecutar tests de contrato contra la nueva versión
- [ ] Verificar compatibilidad hacia atrás para cambios no rotos

### 3.3. Post-Release

- [ ] Monitorear tasas de error y latencia de la nueva versión
- [ ] Recoger feedback de early adopters
- [ ] Actualizar SDKs y librerías cliente
- [ ] Registrar adopción por consumidor para alimentar el próximo plan de deprecación

## 4. Checklist de Cierre

### 4.1. Pre-Cierre

- [ ] Confirmar tráfico cero al endpoint deprecado durante 7 días consecutivos
- [ ] Verificar que todos los consumidores conocidos han migrado (contactar rezagados individualmente)
- [ ] Anunciar fecha final de cierre (aviso de 30 días)

### 4.2. Cierre

- [ ] Deshabilitar el endpoint (devolver `410 Gone` o `404 Not Found`)
- [ ] Eliminar código y tests deprecados
- [ ] Actualizar infraestructura (reglas de load balancer, DNS)
- [ ] Archivar documentación con redirección a la nueva versión

### 4.3. Post-Cierre

- [ ] Monitorear 404s inesperados de consumidores desconocidos
- [ ] Documentar lecciones aprendidas
- [ ] Actualizar línea de tiempo del ciclo de vida de la API

Versiones descargables de este checklist, la guía de migración, un aviso de deprecación rellenado y el script de monitoreo están en el repositorio complementario.

Explicación

El checklist impone un período mínimo de aviso que respeta los cronogramas de los consumidores. Las APIs externas necesitan ventanas de deprecación más largas porque no puedes controlar cuándo los consumidores actualizan — tienen ciclos de release, revisiones de app store y ventanas de cambio congeladas propias. El header Sunset es legible por máquinas, así que las librerías cliente pueden advertir a los desarrolladores automáticamente en lugar de depender de que alguien lea un changelog.

Rastrear el tráfico antes del cierre previene el modo de fallo clásico: un cron interno o una versión olvidada de la app móvil sigue golpeando el endpoint antiguo el día del apagado. Si no puedes nombrar a todos los consumidores de un endpoint deprecado, todavía no tienes un problema de migración — tienes un problema de descubrimiento.

El versionado por URL y el versionado por headers se compensan de forma distinta. Las versiones en URL (/v3/) son explícitas, amigables con la caché y fáciles de buscar en los logs de acceso. Las versiones por header mantienen las URIs estables pero son más difíciles de depurar y fáciles de que un intermediario las elimine. El versionado por URL sigue siendo la opción más común en APIs REST públicas; elígelo salvo que tengas una razón concreta para no hacerlo.

Trade-offs de las Estrategias de Versionado

La decisión de versionado de la sección 3 de la plantilla merece más que una casilla. Cada esquema falla de forma distinta:

EsquemaFortalezasDebilidadesUso típico
Ruta URL (/v3/)Explícito, amigable con caché, trivial de buscar en logsContamina el espacio de URLs; las versiones viejas sobreviven en marcadoresAPIs REST públicas
Header propio (X-API-Version)URLs limpiasInvisible en pruebas de navegador; fácil de que un proxy lo elimineAPIs internas tras un gateway
Media type (Accept: application/vnd.api.v3+json)Versiona la representación, no el recursoIncómodo de probar con curl; peor historia de cachéAPIs hipermedia
Query param (?v=3)Simple de añadirFácil de omitir; comportamiento por defecto débilRara vez recomendable en APIs nuevas

Elijas lo que elijas, aplica un solo esquema de forma consistente. Los esquemas mixtos —URL en unos endpoints, headers en otros— producen consumidores que no saben qué versión están llamando realmente.

Headers de Deprecación y Sunset

Agrega headers HTTP a cada respuesta del endpoint deprecado para que los consumidores descubran la deprecación programáticamente. El header Deprecation (RFC 9745) lleva un timestamp Unix que marca cuándo queda deprecada la API; el header Sunset (RFC 8594) lleva la HTTP-date a partir de la cual el endpoint puede dejar de responder:

HTTP/1.1 200 OK
Content-Type: application/json
Deprecation: @1789430400
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Link: <https://api.example.com/v3/users>; rel="successor-version",
      <https://api.example.com/docs/deprecation-notice>; rel="deprecation"

Los primeros borradores del header Deprecation usaban Deprecation: true; el RFC publicado usa la forma @timestamp. Emite el timestamp y, al parsear, trata cualquier presencia del header como “deprecado”:

function checkDeprecationHeaders(response) {
  const deprecation = response.headers.get("Deprecation");
  const sunset = response.headers.get("Sunset");
  const link = response.headers.get("Link");

  if (deprecation) {
    const since = deprecation.startsWith("@")
      ? new Date(Number(deprecation.slice(1)) * 1000).toISOString()
      : deprecation;
    console.warn(`Endpoint deprecado desde ${since}. Cierre: ${sunset}`);
    if (link) {
      console.warn(`Destino de migración: ${link.match(/<([^>]+)>/)?.[1]}`);
    }
  }
}

Plantilla de Guía de Migración

Proporciona una guía de migración estructurada para cada cambio incompatible:

# Guía de Migración: v2 -> v3 User Service API

## Resumen
- Campo `name` dividido en `firstName` y `lastName`
- Endpoint `/v2/users/{id}` reemplazado por `/v3/users/{id}`
- Respuestas de error ahora usan formato RFC 7807 Problem Details

## Antes (v2)
```json
GET /v2/users/123
{
  "id": 123,
  "name": "Alice Johnson",
  "email": "alice@example.com"
}
```

## Después (v3)
```json
GET /v3/users/123
{
  "id": 123,
  "firstName": "Alice",
  "lastName": "Johnson",
  "email": "alice@example.com"
}
```

## Cambio de Formato de Error
```json
// Error v2
{ "error": "User not found", "code": 404 }

// Error v3 (RFC 7807)
{
  "type": "https://api.example.com/errors/not-found",
  "title": "User not found",
  "status": 404,
  "detail": "User 123 does not exist"
}
```

## Pasos de Migración Automatizados
1. Actualizar URL base de `/v2/` a `/v3/`
2. Reemplazar `name` con `firstName` + `lastName` en modelos de request/response
3. Actualizar manejo de errores para parsear formato RFC 7807
4. Probar contra sandbox en `https://sandbox.api.example.com/v3/`

Script de Monitoreo de Cierre Automatizado

Rastrea el tráfico a endpoints deprecados para saber cuándo es seguro cerrarlos. Este script consulta Prometheus —a través del datasource proxy de Grafana— para obtener la tasa diaria de requests en v2 y cuenta días completos con tráfico cero:

import requests
from datetime import datetime, timedelta, timezone

ZERO_TRAFFIC_DAYS_REQUIRED = 7


def check_sunset_readiness(grafana_url: str, api_token: str) -> bool:
    """Devuelve True cuando v2 sirvió tráfico cero durante los días requeridos."""
    headers = {"Authorization": f"Bearer {api_token}"}
    end = datetime.now(timezone.utc)
    start = end - timedelta(days=ZERO_TRAFFIC_DAYS_REQUIRED + 1)

    params = {
        "query": 'sum(rate(http_requests_total{version="v2"}[1h]))',
        "start": start.timestamp(),
        "end": end.timestamp(),
        "step": 86400,  # un punto de datos por día
    }
    resp = requests.get(
        f"{grafana_url}/api/datasources/proxy/1/api/v1/query_range",
        headers=headers,
        params=params,
        timeout=30,
    )
    resp.raise_for_status()
    series = resp.json()["data"]["result"]

    if not series:
        # Una serie vacía significa que la etiqueta de la métrica desapareció;
        # parece tráfico cero pero puede ser un scrape roto. Verifica antes de actuar.
        print("AVISO: no se devolvió ninguna serie; verifica que la métrica existe")
        return False

    daily_rates = [float(point[1]) for point in series[0]["values"]]
    zero_days = sum(1 for rate in daily_rates if rate == 0)

    if zero_days >= ZERO_TRAFFIC_DAYS_REQUIRED:
        print(f"LISTO PARA CIERRE: {zero_days} días de tráfico cero")
        return True

    print(f"NO LISTO: {zero_days} días de tráfico cero en la ventana")
    print(f"Tasa diaria media: {sum(daily_rates) / len(daily_rates):.2f} req/s")
    return False

Dos detalles justifican las líneas extra. Consulta con un step diario para que una hora tranquila dentro de un día ocupado no se lea como tráfico cero — la versión anterior por horas contaba horas y las comparaba contra días. Y trata un resultado vacío como sospechoso, no como prueba de un endpoint muerto: una etiqueta de métrica perdida se ve exactamente igual que tráfico cero desde fuera.

Comunicar la Deprecación

Una deprecación que los consumidores nunca notan es un apagado sorpresa. Anuncia por todos los canales el mismo día, con las mismas fechas, y mantén el aviso listo para copiar. Un aviso mínimo se ve así:

# Aviso de Deprecación: User Service API v2

**Estado:** Deprecada desde 2026-09-15. Fecha de cierre: 2026-12-31.

**Qué cambia:** `GET /v2/users/{id}` queda reemplazado por `GET /v3/users/{id}`.
El campo `name` se divide en `firstName` y `lastName`, y las respuestas de
error ahora usan RFC 7807 Problem Details.

**Qué hacer:** sigue la guía de migración y prueba contra
`https://sandbox.api.example.com/v3/` antes del 2026-12-01.

**Qué pasa si no lo haces:** después del 2026-12-31 el endpoint devuelve
`410 Gone` con un cuerpo que apunta a la documentación de v3.

**Contacto:** platform-team@example.com — office hours los martes 15:00 UTC.

Envíalo por todos los canales que nombra el checklist — changelog, portal de desarrolladores, email directo y los propios headers Deprecation y Sunset. Reenvía a T-30 y T-7 días con las estadísticas de tráfico reales de ese consumidor. “Tu integración hizo 412 llamadas la semana pasada” consigue una atención que un segundo aviso genérico nunca consigue.

No dependas solo de las bandejas de entrada. Si la API tiene portal de desarrolladores o dashboard, fija el aviso ahí durante toda la ventana de deprecación. Si tus respuestas usan un envelope o un campo de warnings, añade un flag deprecated con la URL de cierre — los consumidores que solo ven tu API a través del código necesitan la señal en el camino del código. En ventanas de deprecación largas, añade una línea a la cadencia mensual de la página de estado o newsletter para que los nuevos consumidores lo sepan antes de integrarse.

Responsables y Aprobaciones

Cada transición del ciclo de vida necesita un aprobador con nombre, o el checklist se convierte en un documento que nadie ejecuta:

  • Deprecación: firma el responsable de la API; los equipos de cara al consumidor acusan recibo de la fecha.
  • Fecha de sunset: el responsable más producto o soporte para APIs externas; legal revisa los contratos de partners antes de publicar la fecha.
  • Cierre: el responsable confirma la evidencia de tráfico cero; un lead de guardia confirma el plan de rollback por si aparece tarde un consumidor crítico.

Escribe nombres en la sección 1 de la plantilla, no solo roles. “Equipo de plataforma” no puede atender un page a las 2 de la mañana.

Variantes

ContextoEnfoqueNotas
Microservicios internosTiempos más cortos, cumplimiento más estrictoLos equipos pueden coordinar vía canal Slack compartido
API pública SaaSTiempos largos, revisión legalPuede requerir compromisos SLA para avisos de deprecación
Backends de app móvilForzar actualización vía app storeUsar verificaciones de versión mínima para cerrar endpoints antiguos
APIs GraphQLDirectivas de deprecación de esquemaUsar directiva @deprecated en campos y tipos
Event-drivenModos de compatibilidad del registro de esquemasTransicionar de BACKWARD a NONE antes de eliminar esquema antiguo
Integraciones partner/B2BPeríodos de aviso contractualesEl MSA prevalece sobre los valores internos — léelo primero

Lo que funciona

  1. Nunca eliminar una API sin período de deprecación, ni siquiera para uso interno
  2. Devolver headers de deprecación tan pronto como se toma la decisión, no en el cierre
  3. Mantener un changelog público de la API con fechas para cada cambio
  4. Versionar el contrato de API independientemente del despliegue del servicio
  5. Mantener endpoints deprecados observables con dashboards dedicados
  6. Enviar avisos de deprecación por múltiples canales (email, headers, changelog, página de estado)
  7. Proporcionar un shim de compatibilidad en migraciones complejas para reducir el esfuerzo del consumidor
  8. Asignar a cada deprecación un responsable con nombre que pueda responder “¿es seguro borrar esto?”

Errores Comunes

  1. Anunciar deprecación pero no rastrear si los consumidores realmente migran
  2. Cambiar comportamiento en una versión existente sin incrementar el número
  3. Eliminar documentación antes de que la API se cierre
  4. Asumir que todos los consumidores leen los avisos por email
  5. Forzar migraciones durante temporadas altas o cierres fiscales
  6. No proporcionar un entorno sandbox para que los consumidores prueben la nueva versión
  7. Cerrar sin monitorear 404s de consumidores desconocidos post-cierre

Troubleshooting

  • Los consumidores siguen llamando al endpoint después de la fecha de cierre: comprueba si el tráfico es shadow traffic, replays o health checks antes de asumir uso real. Si es real, extiende el sunset en lugar de romper a los consumidores en silencio — pero mantén creíble el 410 Gone fijando una nueva fecha final, no una extensión indefinida.
  • Nadie sabe quién sigue usando v2: el registro de consumidores está obsoleto. Reconstrúyelo desde los logs de acceso y las API keys, y haz que el registro forme parte del onboarding de la próxima API.
  • Proliferación de versiones (v1, v2 y v3 todas activas): limita el número de versiones soportadas — dos majors es un techo común — e inicia el reloj de deprecación de la más antigua el día que sale la nueva.
  • SDKs desincronizados con la API: los consumidores migraron sus llamadas pero el SDK sigue renderizando el esquema antiguo. Versiona y publica los SDKs en el mismo checklist que la API, o genéralos desde el contrato.
  • El email de deprecación rebotó: las listas de contactos registrados decaen. Revisa los rebotes semanalmente durante la ventana de deprecación y recurre al header Link y al portal de desarrolladores.

Puntos Clave

  • Una deprecación es un proceso con fechas, responsables y métricas — no un anuncio.
  • Los headers legibles por máquinas (Deprecation, Sunset, Link) llegan a consumidores que nunca leen el email.
  • Cierra basándote en evidencia — tráfico cero durante N días consecutivos — no solo en el calendario.
  • Presupuesta tiempo de migración del consumidor: los consumidores externos se mueven a su ciclo de release, no al tuyo.

Errores Comunes en Producción

Estos aplican al documento de ciclo de vida en sí, no a la API:

  • Dejar campos requeridos vacíos o usar respuestas vagas de una palabra.
  • Llenar el documento una vez y nunca actualizarlo cuando cambia el alcance o las decisiones.
  • Guardar el documento donde el equipo no lo busque durante incidentes o revisiones.
  • No asignar un responsable, fecha límite o cadencia de revisión.
  • Copiar texto base sin eliminar secciones que no aplican.
  • Saltar el control de versiones, lo que impide rollback y responsabilidad.
  • No vincular el documento con decisiones relacionadas o acciones de seguimiento.
  • Evitar revisiones trimestrales que retirarían secciones obsoletas o sin uso.

Lectura Adicional

Preguntas frecuentes

¿Cuánto tiempo debería mantener viva una API deprecada?

APIs externas: mínimo 6-12 meses. APIs internas: mínimo 3 meses. Los contratos enterprise pueden especificar períodos más largos. Nunca deprecar durante períodos de alto tráfico conocidos (Black Friday, temporada de impuestos).

¿Debería usar versionado por URL o por headers?

El versionado por URL (/v1/, /v2/) es explícito y fácil de depurar. El versionado por headers mantiene URLs limpias pero es más difícil de cachear y solucionar. La mayoría de equipos usan versionado por URL para APIs REST.

¿Qué hago si un consumidor se niega a migrar?

Si un consumidor es crítico y no puede migrar a tiempo, negocia una extensión con fecha límite dura. Si el consumidor no es crítico, procede con el cierre; la respuesta 410 Gone forzará la acción.

¿Cómo manejo el versionado para APIs GraphQL?

GraphQL usa un único endpoint. Deprecar campos con la directiva @deprecated y monitorear uso vía consultas de introspección. Eliminar campos deprecados solo después de que el uso baje a cero.

¿Qué es un shim de compatibilidad y cuándo debo usarlo?

Un shim de compatibilidad es una capa de traducción que acepta requests en formato antiguo y los convierte al nuevo formato internamente. Úsalo cuando la migración es compleja (ej. división de campos, reestructuración de respuesta) y los consumidores necesitan tiempo para adaptarse. Elimina el shim después de que todos los consumidores hayan migrado.

¿Debería mantener SDKs separados para cada versión de API?

Mantén SDKs para la versión major actual y la anterior. Elimina soporte para SDKs más antiguos después de que la ventana de deprecación expire. Publica guías de migración junto con actualizaciones de SDK para que los desarrolladores puedan actualizar en una sola pasada.

¿Cómo automatizo la verificación de preparación para cierre?

Instrumenta tu API gateway o load balancer para etiquetar requests por versión. Construye un dashboard que muestre tráfico por versión a lo largo del tiempo. Configura una alerta cuando el tráfico a una versión deprecada baje de un umbral durante 7 días consecutivos, señalando preparación para cierre.

¿Cuál es la diferencia entre deprecada y sunset?

Deprecada significa que el endpoint sigue funcionando pero está programado para eliminarse — los consumidores deben dejar de construir sobre él y planear su migración. Sunset significa que la fecha de cierre está fijada y publicada, normalmente vía el header Sunset. Una API puede permanecer "deprecada" durante meses; una vez que entra en "sunset" la cuenta atrás es pública y la fecha del 410 Gone está comprometida.