Lo difícil no es el primer endpoint: es el segundo cambio
Exponer datos es cuestión de horas. Poder cambiarlos dentro de un año sin romper a quien los consume es lo que cuesta.
Una API deja de ser un detalle de implementación en el momento en que alguien la usa. A partir de ahí cada respuesta es un contrato, y los contratos solo se pueden cambiar de dos maneras: avisando o rompiendo. La diferencia entre una API sostenible y una que da miedo tocar se decide en las primeras semanas, no en el rendimiento.
El error más caro es dejar que el modelo de persistencia se filtre hacia fuera. Cuando se serializan directamente las entidades JPA, cualquier cambio en el modelo de datos se convierte en un cambio de contrato, y renombrar una columna pasa a ser una negociación con tres equipos. El trabajo consiste en poner una capa propia de representación en medio, aunque al principio parezca duplicación. Es la misma frontera que hace posible mover la aplicación a Spring Boot por partes.
La segunda decisión que marca el resto es qué se hace cuando algo va mal. Una API que responde 200 con un mensaje de error dentro del cuerpo obliga a cada consumidor a inventarse su propia lógica de detección, y esa lógica siempre acaba siendo distinta en cada cliente.
Las decisiones que conviene tomar antes de escribir código
No hay una respuesta correcta universal, pero sí hay una respuesta correcta para cada contexto. Lo que no funciona es decidirlo sobre la marcha en cada endpoint.
Síntomas de que una API se va a volver difícil de cambiar
Ninguno es un fallo en ejecución. Todos son decisiones que se pagan más adelante.
Renombrar un campo de una entidad rompe a un consumidor
Causa: El modelo de persistencia se está serializando tal cual. La base de datos y el contrato público son la misma cosa.
Qué se hace: Introducir objetos de representación propios en la frontera. Se puede hacer de forma progresiva, empezando por los recursos que más cambian.
Cada cliente interpreta los errores de una forma distinta
Causa: La API responde con códigos de estado inconsistentes, o mete el error dentro de una respuesta correcta.
Qué se hace: Unificar el formato de error y los códigos de estado, y documentarlos. Un error debe poder tratarse sin leer el cuerpo para saber si lo es.
Un reintento del cliente duplica un pedido
Causa: La operación de creación no es idempotente y el cliente no tiene forma de saber si la primera llamada llegó.
Qué se hace: Aceptar una clave de idempotencia proporcionada por el cliente y devolver la misma respuesta ante una repetición. Evita la mayoría de incidencias de duplicados.
Nadie sabe quién consume un endpoint
Causa: No hay registro de consumidores ni trazas que lo permitan deducir, así que ningún cambio se considera seguro.
Qué se hace: Instrumentar por consumidor antes de tocar nada. Saber quién llama y con qué frecuencia convierte una retirada arriesgada en un plan con fechas.
Lo que debería estar resuelto antes de exponer una API
- Contrato documentado y generado desde el código, no escrito aparte.
- Formato de error único, con códigos de estado coherentes.
- Estrategia de versionado decidida, aunque solo exista la versión uno.
- Autenticación y autorización definidas por recurso, no solo a la entrada.
- Límites de tamaño y de frecuencia, para que un cliente mal programado no tumbe al resto.
- Pruebas de contrato que fallen si la respuesta cambia de forma.
- Trazas que permitan saber qué consumidor hizo qué llamada.
Sobre una aplicación que ya está en producción, esto rara vez se hace de golpe: se introduce recurso a recurso, con el sistema funcionando, dentro del trabajo de modernización de la aplicación.
Cómo se cambia un contrato que ya tiene consumidores
El objetivo es que ningún consumidor tenga que coordinarse con vosotros para seguir funcionando.
Saber quién consume qué
Instrumentar por consumidor y por recurso antes de plantear ningún cambio. Sin ese dato, retirar un campo es una decisión a ciegas y cualquier fecha es inventada.
Añadir antes de quitar
El campo o el recurso nuevo convive con el antiguo. Añadir no rompe a nadie; quitar sí. Esa asimetría es la que permite avanzar sin reuniones de coordinación.
Marcar lo antiguo como obsoleto donde se vea
En la documentación generada y en las trazas. Un aviso que solo existe en un correo enviado hace ocho meses no lo ha leído nadie.
Esperar a que el uso llegue a cero
Con datos, no con una fecha pactada. Cuando las llamadas al recurso antiguo caen a cero y se mantienen así, la retirada deja de ser una negociación.
Retirar y limpiar
Se elimina el código, no se deja desactivado por si acaso. Un camino muerto que sigue ahí vuelve a estar vivo en cuanto alguien lo encuentra.