02Criterio6 min

El documento que escribí antes del código estaba mal, y para eso servía

En integraciones con APIs ajenas escribo primero lo que no controlo y luego dedico un commit entero a comprobar si era verdad. Ese commit descubrió que 18 de 20 issues eran pull requests.

Por Salva Castells

Imagen de portada: Tobin, CC BY-SA 2.0.
En este post

Si miras el historial de un visor de repositorios que hice el verano pasado, la primera semana tiene ocho commits. El primero es el init desde mi plantilla. Los tres siguientes son documentación. El quinto ya es la app entera, de golpe.

Me gustaría decir que fue disciplina, pero fue más bien que al escribir el documento me di cuenta de que no entendía la integración. Y descubrirlo en un .md me costó veinte minutos. Descubrirlo en código me habría costado, calculo, un par de días y unos cuantos 401.

Ya sé que "documenta antes de programar" suena a consejo de consultora, de los que se dan en una charla y nadie sigue. Yo tampoco lo sigo en general. Lo hago en un caso muy concreto, y lo que me interesa contar no es tanto el documento como lo que pasó cuando comprobé si lo que había escrito era verdad.

Solo lo hago cuando hay una API que no es mía

Cuando la lógica es tuya de punta a punta, el código es la mejor documentación que vas a tener y cualquier .md previo envejece mal. No lo escribo. Tampoco para prototipos que voy a tirar: tengo un juego hecho en un día sin una línea de documentación y está perfecto así.

Pero cuando dependes de una API ajena, lo que necesitas saber no está en tu código ni en el suyo. Está en los límites, las cuotas, los pasos manuales y los parámetros que se ignoran en silencio. Y eso no se descubre leyendo, se descubre chocando.

En el visor, por ejemplo, estuve un rato mezclando dos conexiones con GitHub que parecen la misma y no lo son: el OAuth para que entre un humano y el token de servidor para leer la API. Fallan distinto, caducan distinto y tienen límites distintos. En cuanto los separé en dos secciones del documento vi que un visor de repos públicos solo necesitaba el segundo para leer, y que el login era otra decisión aparte.

Lo que pongo en ese documento

Nada sobre el diseño de mi app. Solo lo que no controlo, y si puede ser, medido.

Los límites, pidiéndoselos al endpoint de cuota en vez de copiarlos de la web: 60 por hora sin autenticar, 5.000 autenticado, 30 por minuto en búsqueda. Y la parte que de verdad justificaba el documento: medir que un 304 Not Modified no gasta cuota. Lancé la misma petición con If-None-Match varias veces y el contador restante se quedó en 4972, 4972, 4972. La misma llamada sin la cabecera condicional lo bajó a 4971. Ese número repitiéndose sostiene toda la estrategia de caché del proyecto, y no lo tenía hasta que lo ejecuté.

Los pasos que no se pueden automatizar. La OAuth App no se crea por CLI: no hay endpoint para crearla ni para leer el client secret, que te enseñan una vez en el navegador y nunca más. El día que tengas que rehacer el entorno lo habrás olvidado, te lo garantizo.

Los topes que no ves en el camino feliz: 2.000 creaciones de token por hora, que es un techo de logins y no de peticiones, y la reautorización forzada si un mismo usuario entra diez veces en una hora.

Y lo que la decisión no resuelve, que es lo que más me cuesta escribir porque va contra mis ganas de dar el tema por cerrado. Por ejemplo, anoté que obligar a login no quitaba la necesidad de caché condicional, porque la cuota del usuario se comparte con su PAT y con cualquier otra app que actúe en su nombre. Sin esa nota, la decisión de "pedimos login y ya tenemos 5.000" parecía más segura de lo que es.

El commit que corrige el documento

Aquí viene lo que me parece más útil de todo esto, y lo que nadie hace (yo tampoco lo hacía).

Un documento escrito antes del código lleva dentro tus suposiciones, y las suposiciones tienen muy buena cara cuando están bien maquetadas. Así que después de escribirlo hay un commit cuyo único trabajo es verificarlo contra la API real. En el visor ese commit hizo cosas bastante tontas y muy rentables:

  • ejecutar literalmente la consulta que aparecía en la guía, en vez de fiarme de que estaba bien escrita
  • mandar una versión de API inválida a propósito, para que la propia API me devolviera la lista de versiones válidas
  • comprobar que per_page=500 no da error, sino que se recorta a 100 en silencio
  • tipar el snippet del documento que, en modo estricto, no compilaba

Y encontró la mejor: de 20 "issues" que devolvía el endpoint de issues, 18 eran pull requests. La API de GitHub las mezcla, y yo lo sabía vagamente, pero en el documento había escrito el conteo como si fueran issues. Si eso llega al código, el visor enseña números que no significan lo que dicen, y nadie se queja porque parecen razonables.

La otra corrección fue más gorda y más vergonzosa. En la sección de hosting yo había escrito una plataforma serverless. Nadie lo había decidido; lo di por hecho al redactar el encargo. Al revisarlo, la conclusión cambiaba entera: en serverless el SDK de agentes que quería usar no cabía, y en un contenedor de larga vida con quince minutos de límite por petición, sí. Esa frase la había escrito yo sin pensarla, y habría condicionado toda la arquitectura.

Por eso ahora el documento previo no me parece valioso por sí mismo. Lo valioso es tener algo concreto que pueda estar equivocado, para poder ir a comprobarlo. Si lo que tengo es una idea en la cabeza, no sé ni qué verificar.

El mismo truco en sitios que no son APIs

Me ha pasado algo parecido con cosas que no son integraciones.

En una galería de conceptos de UI escribí primero una guía de la pieza (tallas, escalera de espaciado, escala de grises, qué primitivo va para qué) y luego audité el código contra ella. Lo curioso es que la auditoría acabó corrigiendo más la guía que el código. El gris acompañante tenía cinco escalones, no dos. La escalera de espaciado tenía cuatro peldaños, no tres, y llamarla de tres convertía seis usos perfectamente legítimos en "deriva" que había que arreglar. Y los tres diálogos eran en realidad el mismo, con la misma caja y la misma cabecera; como la guía no lo decía, una corrección se había aplicado a uno y no a los otros dos.

Sin la guía no habría encontrado nada de eso. Habría tenido opiniones, y un "a mí me parece" no sirve para auditar.

En un servicio de IA de análisis documental hice una variante: antes de meter ninguna pieza en el servicio, cada una tenía un cookbook, un script que la ejecuta sola, deja su salida en una carpeta y se registra en un run_all_cookbooks. Parser de PDF, chunker, embeddings, detector de números, extracción verificable. Cuando la salida del chunker cambiaba de forma, lo veía en un fichero de texto y no como un error raro tres capas más arriba. Y cuando pasé el pipeline a asíncrono, los convertí en runners con asyncio.run y migré pieza a pieza.

Eso sí, hay que matarlos. Meses después tengo un commit borrando cookbooks deprecados, y reconozco que alguno sobrevivió más de lo que debía. Un cookbook que ya no refleja la pieza es peor que no tenerlo, porque te enseña un mundo que ya no existe con toda la confianza del mundo.

Cuándo no lo haría

Si el documento va a repetir lo que dice la referencia oficial, no lo escribas; solo aporta si lleva tus mediciones, tus versiones y las trampas que te han tocado a ti. Si la integración es tuya, tampoco. Y si no vas a hacer el commit de verificación, casi que mejor no lo escribas: te quedas con una suposición bien formateada que además te da confianza, que es lo peor que te puede dar.

No tengo claro que esto escale a un equipo grande, donde el documento lo escribe uno y lo verifica otro y lo mantiene un tercero que no estaba. Para mí, trabajando solo o con poca gente, es de las pocas costumbres que no he abandonado.