Software Architect · Módulo 07

Un API es una promesa entre sistemas. Un buen contrato sobrevive a clientes, equipos y varias versiones de la implementación.

REST · GraphQL · gRPC · idempotency · compatibility

§ 01

El transporte importa, pero el contrato importa más: qué aceptamos, qué devolvemos, qué errores son posibles y qué se puede reintentar sin riesgo.

Un API debe ser aburridamente predecible

Un buen contrato no impresiona por su estilo. Es claro, completo y no deja ambigüedades peligrosas.

REST, GraphQL, gRPC y WebSocket resuelven problemas distintos. REST es simple para operaciones resource-oriented. GraphQL le da al cliente flexibilidad sobre la forma de la respuesta. gRPC sirve para llamadas typed service-to-service. WebSocket encaja en canales bidireccionales en tiempo real.

Pero el diseño profesional de un API no empieza por el transporte. Empieza por el schema, los códigos de status, los errores, la idempotency, los rate limits, la backwards compatibility y la deprecation policy.

Los errores son parte del contrato

Si el GPS solo dice "no se pudo", el conductor no sabe si se quedó sin gasolina, si la vía está cerrada o si escribió mal la dirección.

El cliente necesita errores machine-readable: un código, un mensaje para el developer, el campo problemático, retryability y un correlation id. Un 400 Bad Request pelado, sin estructura, obliga al cliente a parsear texto o a adivinar.

Los errores tienen que ser tan estables como una respuesta exitosa. Si un sistema downstream construye su comportamiento sobre PAYMENT_REQUIRES_ACTION, no puedes cambiarlo mañana por un string arbitrario.

§ 02

El mundo real reintenta: timeouts, retries, doble click, webhooks duplicados, reinicio de workers.

Ejemplo: creación idempotente de un pago

Si alguien pulsa dos veces el botón del ascensor, no debería llegar dos veces y irse dos veces. La intención es una sola.

El endpoint POST /payments acepta un Idempotency-Key. El servidor guarda la clave, el fingerprint de la request y el resultado de la operación. Repetir la misma request devuelve el mismo payment intent. Repetir con la misma clave pero distinto body devuelve un conflicto.

Ese contrato protege el dinero, la UX y al equipo de soporte. El cliente puede hacer retry sin riesgo después de un timeout.

Antiejemplo: un breaking change disfrazado de cleanup

Si cambias la cerradura del portal sin avisar, el problema no es que la cerradura nueva sea mejor.

El equipo renombra userId a accountId porque es "más correcto". El frontend interno se actualizó; los clientes externos se rompieron. Sin versión, sin changelog, sin ventana de migración, sin deprecation warning.

Un buen API respeta las promesas que ya hizo. El modelo nuevo puede ser mejor, pero el camino de migración es parte de la decisión.

Autoevaluación
  • ¿Qué errores puede manejar el cliente de forma automática? - ¿Qué pasa cuando se repite la request? - ¿Cómo agregamos un campo sin un breaking change? - ¿Dónde está escrita la deprecation policy?