StackPractices
beginner Por Mathias Paulenko

Plantilla de Aviso de Deprecación de API

Plantilla para comunicar deprecaciones de API, cambios breaking y plazos de retiro a los consumidores.

Resumen

Las APIs evolucionan. Los campos se renombran, los endpoints se reemplazan y las versiones antiguas se retiran. Sin un aviso de deprecación claro, los consumidores descubren los cambios breaking solo después de que sus integraciones fallan. Esta plantilla te da un formato estándar para anunciar deprecaciones, comunicar plazos y guiar a los consumidores a través de las migraciones. Recursos relacionados: Stream de salida LLM con Server-Sent Events (SSE), Cómo documentar una API con OpenAPI, Swagger UI y Redoc e Implementar logging y audit trails de API.

Cuándo Usar

Usa este recurso cuando:

  • Elimines o renombres un endpoint, campo o parámetro de API
  • Cierres una versión completa de API
  • Migres consumidores de un servicio legacy a un reemplazo
  • Actualices mecanismos de autenticación que rompan clientes existentes

Solución

# Aviso de Deprecación de API: `<Endpoint / Campo / Versión>`

**API:** `api.ejemplo.com/v1/...`
**Deprecado Desde:** `2026-07-01`
**Fecha de Retiro:** `2026-10-01` (aviso de 92 días)
**Severidad:** `Cambio Breaking` | `Deprecación No Breaking`

## Qué Está Cambiando

### Antes

GET /v1/orders?customer_id=123 Response: { “order_id”: “abc”, “total”: 100.00 }


### Después

GET /v2/orders?customerId=123 Response: { “orderId”: “abc”, “totalAmount”: 100.00 }


## Por Qué Este Cambio

- Alinear los nombres de campos con el estándar camelCase de la empresa
- Consolidar los modelos de datos v1 y v2 para reducir mantenimiento
- Eliminar campos deprecados que exponen identificadores internos

## Pasos de Migración

1. **Actualizar nombres de campos:** Renombrar `customer_id` a `customerId`, `order_id` a `orderId`
2. **Actualizar parsing de respuesta:** Reemplazar `total` por `totalAmount` (mismo tipo de dato)
3. **Cambiar endpoint:** Cambiar la ruta base de `/v1/orders` a `/v2/orders`
4. **Probar en sandbox:** Validar la integración contra `sandbox-api.ejemplo.com/v2`
5. **Desplegar a producción:** Antes del `2026-10-01`

## Cronograma

| Hito | Fecha | Acción Requerida |
|------|-------|-------------------|
| Aviso Enviado | 2026-07-01 | Revisar guía de migración |
| Sandbox Disponible | 2026-07-01 | Comenzar pruebas de endpoints v2 |
| v1 Marcado Deprecado | 2026-07-01 | Monitorear headers de deprecación |
| Recordatorio Final | 2026-09-01 | Completar migración o solicitar extensión |
| Retiro de v1 | 2026-10-01 | v1 retorna 410 Gone |

## Soporte y Contacto

- **Guía de Migración:** https://docs.ejemplo.com/api-migration
- **Entorno Sandbox:** https://sandbox-api.ejemplo.com
- **Email de Soporte:** api-soporte@ejemplo.com
- **Horarios de Atención:** Cada martes 10:00 UTC

## Excepciones

Si no puedes migrar antes de la fecha de retiro, contáctanos en api-soporte@ejemplo.com con:
- Tu caso de uso
- Cronograma estimado de migración
- Bloqueantes que impidan la migración a tiempo

Explicación

La plantilla separa qué está cambiando de por qué y cómo migrar. La tabla de cronograma crea responsabilidad y elimina ambigüedad sobre las fechas límite. Incluir un entorno sandbox y contacto de soporte reduce la fricción para los consumidores. La sección de excepciones reconoce que no todos los consumidores pueden migrar en el mismo plazo.

Cómo Adaptar Esta Plantilla

El mismo esqueleto funciona para cada deprecación, pero tres decisiones cambian lo que envías:

Severidad. Un cambio breaking (endpoint eliminado, campo renombrado, cambio de esquema de autenticación) necesita el aviso completo: pasos de migración, tabla de cronograma, enlace a sandbox y un contacto con nombre. Una deprecación no breaking (un campo nuevo preferido junto al antiguo, un parámetro opcional que desaparece) puede reducirse a un aviso más corto: qué cambió, cuándo desaparece y dónde leer más. No des la voz de alarma en vano: si marcas cada deprecación como “Cambio Breaking”, los consumidores dejan de leer el banner.

Audiencia. Los consumidores externos necesitan fechas con las que puedan planificar — trimestres, no semanas — y un humano al que escalar. Los consumidores internos toleran ventanas de 30 días y un post en Slack, pero siguen necesitando los pasos de migración; el “ya lo resolveremos” es la razón por la que las v1 internas viven para siempre. Si tu API tiene ambas audiencias, escribe primero el aviso público y recórtalo para el canal interno, no al revés.

Canal. El email llega a las personas que se registraron; los headers de respuesta llegan a las integraciones que realmente te llaman; los dashboards llegan a los humanos que depuran a las 2 de la madrugada. Necesitas los tres, y por eso el plan de comunicación de abajo los escalona en lugar de enviar un solo anuncio.

Para calibrar, mira cómo lo anuncian las APIs públicas: la documentación de versionado de Stripe trata cada cambio como fechado y opt-in, GitHub publica posts de deprecación con meses de antelación y horas de corte UTC exactas, y los servicios de API de Google publican calendarios de deprecación explícitos por versión. El patrón común es que la fecha límite es una promesa: elige una fecha que realmente puedas cumplir.

Avisos de Deprecación Reales

Tres deprecaciones públicas que vale la pena estudiar antes de escribir la tuya:

GitHub: autenticación por contraseña para operaciones Git. GitHub anunció en 2019 que la autenticación por contraseña para la API moriría, dio más de un año de aviso y — la parte que vale robarse — ejecutó brownouts programados: durante unas horas cada vez, rechazaron los requests autenticados con contraseña para que las integraciones fallaran ruidosamente antes del corte real. Quien siguiera usando contraseña lo descubrió durante el brownout, no después del retiro. El corte final se movió una vez, y la nueva fecha se anunció de la misma forma.

Kubernetes: la política de deprecación es el aviso en sí. Kubernetes publica una política de deprecación versionada: las APIs beta reciben aproximadamente 9 meses o 3 releases de soporte tras la deprecación, las versiones GA más tiempo. Los consumidores no necesitan un email para planificar; la política ES el cronograma. Es el estado ideal para una API con una base grande de consumidores: las reglas de deprecación son públicas, versionadas y aburridamente predecibles.

Stripe: versiones fechadas en lugar de retiro. Stripe evita el problema por completo para cambios pequeños: cada cuenta queda anclada a la versión de API vigente cuando fue creada, y los consumidores actualizan a su propio ritmo. También retiran versiones antiguas eventualmente, pero el anclaje de versión hace que el aviso de deprecación aterrice en un codebase que ya está probando la versión nueva, no en uno que acaba de romperse.

El patrón en los tres casos: la fecha límite es real, la advertencia llega por el canal que el consumidor realmente usa, y hay un ensayo (brownout, versión anclada, política publicada) antes del corte definitivo.

Implementación de Headers de Deprecación

Los headers HTTP Deprecation y Sunset (estándar IETF en borrador) permiten que el código cliente detecte endpoints deprecados programáticamente. Impleméntalos en tu middleware de API.

Middleware en Express.js

function deprecationMiddleware(req, res, next) {
  const deprecatedPaths = {
    "/v1/orders": { sunset: "2026-10-01", replacement: "/v2/orders" },
    "/v1/products": { sunset: "2026-10-01", replacement: "/v2/products" },
  };

  const match = Object.keys(deprecatedPaths).find((path) =>
    req.path.startsWith(path)
  );

  if (match) {
    const info = deprecatedPaths[match];
    res.setHeader("Deprecation", "true");
    res.setHeader("Sunset", new Date(info.sunset).toUTCString());
    res.setHeader(
      "Link",
      `<https://docs.ejemplo.com/api-migration>; rel="deprecation"`
    );
  }

  next();
}

app.use(deprecationMiddleware);

Middleware en Python Flask

from datetime import datetime
from flask import Flask, request, g

DEPRECATED_PATHS = {
    "/v1/orders": {"sunset": "2026-10-01", "replacement": "/v2/orders"},
    "/v1/products": {"sunset": "2026-10-01", "replacement": "/v2/products"},
}

@app.before_request
def add_deprecation_headers():
    for path, info in DEPRECATED_PATHS.items():
        if request.path.startswith(path):
            g.deprecation_sunset = info["sunset"]
            g.deprecation_replacement = info["replacement"]
            break

@app.after_request
def set_deprecation_headers(response):
    if hasattr(g, "deprecation_sunset"):
        response.headers["Deprecation"] = "true"
        response.headers["Sunset"] = datetime.strptime(
            g.deprecation_sunset, "%Y-%m-%d"
        ).strftime("%a, %d %b %Y 00:00:00 GMT")
        response.headers["Link"] = (
            '<https://docs.ejemplo.com/api-migration>; rel="deprecation"'
        )
    return response

Rastreo del Progreso de Migración

Vigila el tráfico en los endpoints deprecados: es la única forma fiable de saber quién no ha migrado, porque nadie lee el email.

Consulta SQL para Tráfico de Deprecación

SELECT
    endpoint,
    COUNT(*) AS request_count,
    COUNT(DISTINCT client_id) AS unique_clients,
    MAX(timestamp) AS last_request
FROM api_requests
WHERE endpoint LIKE '/v1/%'
    AND timestamp >= NOW() - INTERVAL '7 days'
GROUP BY endpoint
ORDER BY request_count DESC;

Alertas para Consumidores sin Migrar

Configura una alerta cuando un consumidor con tráfico significativo no haya comenzado a migrar:

alert: stale_deprecation_consumer
expr: |
  sum by (client_id) (
    rate(api_requests_total{endpoint=~"/v1/.*"}[1h])
  ) > 10
for: 24h
labels:
  severity: warning
annotations:
  summary: "Cliente {{ $labels.client_id }} aún usa endpoints v1 deprecados"
  description: "Este cliente ha hecho >10 req/h a endpoints v1 en las últimas 24h"

Plan de Comunicación

CanalMomentoAudienciaContenido
Email masivoT-90 díasTodos los consumidores registradosAviso completo de deprecación + enlace a guía
Blog postT-90 díasPúblicoAnuncio + contexto del cambio
Headers en respuesta APIT-90 díasIntegraciones activasDeprecation: true, header Sunset
Banner en panelT-60 díasUsuarios del panelBanner persistente con fecha límite
Email de seguimientoT-30 díasConsumidores no migradosRecordatorio + ofrecer horarios de atención
Contacto directoT-14 díasAlto tráfico no migradoEmail o llamada personal del equipo de API
Email finalT-7 díasTodos los restantes”Retiro de v1 en 7 días”
Página de estadoT-0Todosv1 retorna 410 Gone, actualización de estado
Mermaid flowchart TD diagram

El punto del escalonamiento no es dar la lata: es que cada canal atrapa a un grupo distinto. El email va a quien registró la key. Los headers van al código que te llama. El banner atrapa al humano que entra a depurar. El contacto directo es para el puñado de consumidores que generan suficiente tráfico como para importar pero no han respondido a nada más.

Variantes

ContextoEnfoqueNotas
API PúblicaAviso de 90+ días, blog post, emailLa confianza del consumidor depende de plazos predecibles
API InternaAviso de 30 días, anuncio en SlackIteración más rápida, base de consumidores menor
Parche de seguridad de emergenciaAviso de 7 días, contacto directoLa seguridad tiene prioridad sobre la conveniencia
API GraphQLDirectiva @deprecated + avisoDeprecación a nivel de esquema junto con comunicación

Lo que funciona

  1. Enviar headers de deprecación en respuestas de API al menos 90 días antes del retiro (Deprecation: true, Sunset: <fecha>)
  2. Proporcionar un reemplazo funcional antes de eliminar el endpoint antiguo
  3. Mantener un changelog con todas las deprecaciones y migraciones
  4. Rastrear el progreso de migración monitoreando el tráfico a endpoints deprecados
  5. Ofrecer horarios de atención o una guía de migración para cambios complejos
  6. Usar múltiples canales de comunicación: el email solo no es suficiente
  7. Registrar el uso de headers de deprecación para saber qué clientes están al tanto

Errores Comunes

  1. Anunciar deprecación sin un reemplazo: los consumidores no tienen a dónde ir
  2. Plazos de aviso demasiado cortos: los clientes enterprise necesitan trimestres para planificar cambios
  3. Cambiar comportamiento silenciosamente sin anunciar deprecación primero
  4. No rastrear qué consumidores aún usan endpoints deprecados
  5. Eliminar sin período de gracia: retorna siempre 410 Gone primero
  6. Enviar un solo email y asumir que todos lo leyeron: usa múltiples canales
  7. No proporcionar sandbox para que los consumidores prueben el nuevo endpoint
  8. Extender la fecha de retiro repetidamente: socava la confianza en futuros plazos
  9. Olvidar actualizar SDKs y librerías cliente junto con el cambio de API

Troubleshooting

  • Los consumidores dicen que nunca recibieron el aviso: no leyeron el email. Es lo esperado, y por eso existen los headers, el banner y los recordatorios. Revisa tus logs de requests: si un cliente sigue llamando a v1 en T-30, le toca un recordatorio; en T-14 con tráfico real, le toca una persona.
  • La integración de un cliente se rompe antes de la fecha de retiro: normalmente un campo renombrado que se desplegó antes de tiempo o un cliente que parsea respuestas con demasiada rigidez. Compara la respuesta de v1 con lo que documentó el aviso: si el aviso era correcto, la corrección está de su lado y tu guía de migración lo cubre; si no, les debes un aviso actualizado.
  • Un consumidor importante no puede migrar a tiempo: concede una extensión por escrito con fecha de expiración, regístrala en el log de deprecación y hazlo la primera línea de tu aviso final. Nunca extiendas el retiro público para todos por acomodar a un solo consumidor: eso enseña al resto a ignorar tus plazos.
  • El tráfico a v1 no ha bajado en T-30: probablemente tus headers de deprecación no están llegando a los clientes. Verifícalo con un curl contra un endpoint deprecado: si Deprecation: true no aparece en la respuesta, el middleware no está desplegado donde realmente termina el tráfico (load balancer, gateway o una ruta legacy que lo esquiva).
  • Alguien pide una excepción después del retiro: la respuesta ya está en la sección de excepciones — no existe “una semana más” una vez que 410 Gone empieza a retornar, porque la alternativa es enseñar a cada aviso futuro que tus plazos son negociables.

Ver También

Código complementario: recursos de aviso de deprecación de API: la plantilla, el middleware y las consultas de rastreo de esta página.

Preguntas frecuentes

¿Cuánto aviso debo dar?

APIs públicas: 90-180 días. APIs internas: 30-60 días. Cambios relacionados con seguridad: tan rápido como sea posible con contacto directo.

¿Debería soportar ambas versiones indefinidamente?

No. Cada versión que mantienes viva es más código que parchear, más superficie que proteger y más documentación que mantener honesta. Elige una fecha firme de retiro y cúmplela, con excepciones limitadas.

¿Qué código de estado HTTP debería retornar un endpoint deprecado después del retiro?

Retorna 410 Gone: dice 'esto desapareció para siempre' en lugar de 'quizá sea un bug'. Apunta a los consumidores al reemplazo en el body o en un header Link para que no se queden adivinando.

¿Qué pasa si un cliente importante no puede migrar a tiempo?

Ofrece una extensión temporal con una fecha de expiración documentada. Registra la extensión en tu log de deprecación. No extiendas indefinidamente: eso anula el propósito del retiro.

¿Debería retornar advertencias durante el período de deprecación?

Sí. Retorna 299 Miscellaneous Persistent Warning con un mensaje de deprecación en el header Warning. Es una señal suave que no rompe a los clientes pero aparece en los logs.

¿Cómo depreco un campo de GraphQL?

Usa la directiva @deprecated en tu esquema:

type Order {
  total: Float @deprecated(reason: "Usar totalAmount en su lugar. Eliminado en 2026-10-01.")
  totalAmount: Float
}

Los clientes de GraphQL ven la advertencia directamente en sus resultados de introspección, y por eso la deprecación a nivel de esquema le gana a un aviso para GraphQL.

¿Debería versionar toda la API o solo los endpoints cambiados?

Versiona el endpoint cuando cambia un endpoint; sube la versión completa de la API solo cuando un cambio coordinado afecta a la mayoría de endpoints a la vez. Los saltos de versión completos cuestan a todo el mundo, así que no gastes uno en un rename.

¿Cómo manejo la deprecación en una integración basada en webhooks?

Envía un evento de webhook deprecation.notice a cada endpoint suscrito con el mismo payload que el aviso: qué cambió, la fecha de retiro, el enlace de migración. Después envía eventos de recordatorio en T-30 y T-7, porque los consumidores de webhooks también ignoran el email.