1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Cuando una API web pública usa a la vez un nombre como Product API y una ruta /api/v1, la versión semántica de la propia API y su estructura pueden desalinearse
  • Si se usan en paralelo la ruta /v1/ y major.minor.patch, se mezclan las rutas con el contrato de la API, y el primer número de la versión semántica queda fijado en la URL
  • Los cambios que rompen compatibilidad pueden requerir una ruta nueva y rutas de proxy inverso, por lo que la información del contrato puede quedar dispersa entre la URL y el número de versión
  • Si se crean APIs posteriores al mismo tiempo, la API existente en la práctica queda atada a v1, y en cambios posteriores que rompan compatibilidad el significado del nombre y la ruta se vuelve ambiguo
  • Se plantea la inquietud de encontrar, para el versionado de APIs web públicas, una alternativa menos molesta a este patrón repetido y principios de diseño mejores

1 comentarios

 
GN⁺ 4 시간 전
Opiniones en Lobste.rs
  • Poner /v1/ en la URL en realidad tiene una gran ventaja: te obliga a no romper la API para los usuarios hasta que apagues ese endpoint

  • Otros textos del mismo autor, como Evolving HTTP APIs, ofrecen consejos útiles

  • Básicamente, se agrega /v1/, /v2/, etc. a cada ruta para indicar cambios incompatibles. Si es una API pública operativa y no un estándar que deba funcionar en múltiples hosts, no hay mucha razón para usar versionado semántico completo (semantic versioning)
    El versionado semántico existe para que otros desarrolladores puedan actualizar dependencias con confianza sin tener que leer el changelog durante 20 minutos, pero en una API en producción la gente no puede elegir cuándo adoptar una nueva versión menor o de corrección
    Se considera cambio incompatible cuando se modifica un comportamiento documentado o se rompen clientes existentes que dependen de ese comportamiento documentado. Hay quienes también consideran incompatibles los cambios en comportamientos no documentados, pero eso trae muchos riesgos

  • En Google lo hacen así: AIP-185: API Versioning, AIP-180: Bacwards compatibility
    Siento que estos documentos de diseño están bastante adaptados a la forma de trabajar de Google, pero los he usado como referencia al diseñar APIs y varias de sus ideas me parecieron muy útiles

  • En general, creo que toda API debería intentar minimizar al máximo los cambios incompatibles. Por ejemplo, si quieres cambiar el nombre de una propiedad, me parece mejor agregar el nombre nuevo en paralelo en vez de eliminar la propiedad anterior
    Dicho eso, la forma en que la gente de Buttondown lo hace también es limpia. Definen migraciones entre versiones de la API, de modo que el consumidor puede fijar su versión mediante un header y el proveedor puede seguir haciendo cambios

    • Para propiedades de salida, duplicar propiedades ha funcionado bastante bien. Pero en la entrada hay que manejar el caso en que el cliente envíe ambas propiedades con valores distintos
      La respuesta obvia parece ser “el nombre nuevo siempre tiene prioridad”, pero eso puede fallar cuando el cliente hace una secuencia de lectura-modificación-escritura y vuelve a enviar una versión modificada del objeto creado por el servidor. El cliente podría actualizar solo la propiedad vieja y devolver la nueva sin cambios, tal como venía
    • Como explicó el proveedor de la API, ofrecer migraciones entre versiones de la API suena bien, pero usar un header HTTP para versionar puede causar problemas
    • Ese enlace explica muy bien cómo manejar la forma de los datos. Pero eso es solo una parte; me pregunto qué se hace cuando cambia el comportamiento en sí
      Parecería que esas transformaciones también podrían usarse para asignación de comportamiento, pero salvo que se me haya pasado algo, eso no se trataba ahí
  • Idealmente, la versión debería incluirse en la ruta, y las versiones nuevas deberían tener un carácter aditivo. Así, la API de la versión vieja podría reenrutar la solicitud hacia una versión más nueva de la API aplicando las transformaciones de entrada y salida necesarias
    Si después de unos años nadie usa una versión antigua, se puede eliminar y la ruta /v1/ pasaría a dar error

  • Hace tiempo leí un poco sobre versionar APIs mediante negociación de contenido con el header Accept. Si alguien ha versionado APIs de esa forma, me interesaría conocer su experiencia
    En mi experiencia, el versionado por recurso o el versionado global han sido de las formas más intuitivas. Para la deprecación, usar el header de respuesta HTTP Deprecation (RFC 9745) y eventualmente devolver respuestas como 410 Gone en endpoints viejos parece una forma razonable de empujar a los clientes a migrar a la nueva versión
    Además, de verdad me da curiosidad si alguien ha construido una API evolutiva: una que traduzca internamente las solicitudes de versiones antiguas a solicitudes de una versión más nueva de la API, y luego elimine de verdad la versión antigua una vez que los clientes migren o pase cierto tiempo