Blog ·
Dos reglas y un enlace muerto en medio
Un rastreo de 1776 páginas encontró exactamente 18 URL rotas. Las dos reglas que las produjeron eran correctas; nadie probó qué pasa donde se juntan.
Por Provibr
Rastreamos el sitio de producción a fondo por primera vez en bastante tiempo: cada enlace interno seguido, cada alternativa de idioma declarada comprobada, cada bloque de datos estructurados validado, cada imagen revisada en busca de texto alternativo.
1776 páginas, 1750 entradas de sitemap y exactamente un defecto. Cero huérfanos, cero cadenas de redirección, cero errores de alternativa de idioma sobre 17218 declaraciones, cero bloques de datos estructurados inválidos sobre 1642, cero imágenes sin texto alternativo sobre 15463, y 920 imágenes de vista previa social respondiendo todas 200.
El único defecto eran 18 URL que devolvían 404: las páginas de documentación de dos paneles de facturación, en los nueve idiomas. Cada una de esas 18 tenía exactamente un enlace interno apuntándole, desde la página de integración del mismo producto en el mismo idioma.
Las dos mitades de eso son a propósito. Eso es lo que hace que merezca la pena dejarlo por escrito.
Las dos reglas
Algunas de nuestras integraciones están detrás de un interruptor de despliegue. Cuando el interruptor está apagado, el proveedor desaparece del catálogo, del sitemap, de los contadores y del selector de tipo del panel, todo derivado de una sola tabla para que no haya una segunda lista que olvidar.
Dos de ellas no son del todo corrientes. Para FOSSBilling y Paymenter hay un módulo que se instala en su panel, y la página en /integrations/fossbilling describe ese módulo. No es una página sobre una conexión que ofrecemos; es una página sobre algo que se puede descargar hoy mismo. Por eso hay una regla que mantiene en pie esas dos páginas de integración cuando el proveedor que hay detrás del interruptor está oculto, y lleva su motivo escrito en el código.
La página de documentación es el caso contrario. /docs/fossbilling describe la conexión, que es exactamente lo que el interruptor apaga, así que desaparece con el proveedor.
Las dos reglas están bien. Un solo slug, dos superficies, dos respuestas, y el código tiene dos funciones en lugar de una que estaría equivocada la mitad de las veces.
Lo que nadie había comprobado es qué pasa entre ellas. La página que sobrevive llevaba el enlace temático a la que había desaparecido.
La regla ya estaba por escrito
La función que produce ese enlace resuelve una ruta de documentación para un slug de integración. Su propio comentario de documentación decía, antes de que pasara nada de esto, que un enlace a una guía que no existe no es un enlace temático sino uno muerto.
La regla estaba ahí. Se medía contra la lista equivocada: el conjunto de páginas de documentación que existen en el código, en lugar del conjunto que responde en esta compilación. Esos dos son el mismo conjunto casi todo el tiempo, y el día en que difieren es el día en que algo está oculto.
La reparación es una línea. Esa no es la parte interesante; la parte interesante es que una regla correcta, escrita con todo detalle en el sitio que le corresponde, no es un control. La prosa de un comentario describe una intención. Solo algo que se ejecuta puede exigir su cumplimiento.
Un tercer caso que ningún rastreo podría haber encontrado
Escribimos una guarda en lugar de limitarnos a arreglar la línea: para cada slug de integración, la ruta de documentación resuelta tiene que ser una que esta compilación sirva de verdad. Se demostró primero en rojo, e informó de las dos páginas que había encontrado el rastreo.
También informó de una tercera: nuestra integración con el servidor de copias de seguridad, que está detrás de otro interruptor. Su página de documentación está oculta, y el enlace hacia ella se construía exactamente igual.
El motivo por el que ningún rastreador encontrará nunca ese caso es que su página de integración también está oculta. El enlace muerto vivía en una página que devuelve 404 ella misma, así que no hay ningún camino desde fuera hasta lo que está mal. Habría salido a la luz el día en que alguien encendiera ese interruptor y publicara una página con un enlace roto dentro, y los dos sucesos habrían estado separados por semanas.
Un rastreador solo puede encontrar defectos en páginas alcanzables. Escrito así suena evidente y es fácil de olvidar cuando un rastreo vuelve limpio, porque un rastreo limpio parece una afirmación sobre el código y no sobre las páginas que resultaban ser visibles.
La guarda tiene una prueba más que no hace nada salvo mantener honesta a la primera: afirma que más de diez slugs resuelven de verdad a una página de documentación. Sin ella, un cambio que hiciera que el resolvedor no devolviera nada para ningún slug produciría cero enlaces muertos y cero enlaces temáticos, y esas dos cosas se leen igual en una ejecución de pruebas en verde.
La otra mitad: contarle a un buscador que algo ha cambiado
La misma ronda añadió un canal de notificación, que es una cosa pequeña con una sorpresa dentro.
El protocolo es un único POST que dice a varios buscadores que una URL ha cambiado. La propiedad del host se demuestra sirviendo una clave como fichero de texto en la raíz. Nuestra clave viene del entorno, y cuando no está, el mecanismo entero no hace nada y lo dice una vez. Ese es el estado normal en desarrollo y en todas las pruebas.
Dos detalles merecen repetirse porque los dos son trampas.
El fichero de la clave se sirve mediante una reescritura, porque una ruta dinámica no puede estar en la raíz junto al segmento de idioma. Una reescritura que capturara cualquier cosa punto txt en la raíz se tragaría también el fichero robots. Dos cosas lo impiden: la reescritura se ejecuta en la fase que solo ve las peticiones a las que no correspondió ningún fichero real, y el patrón de la clave exige al menos ocho caracteres, mientras que robots tiene seis y llms cuatro. La cota inferior no es pulcritud; es el segundo cerrojo.
Y la notificación cuelga de la función que ya sabe que ha cambiado contenido público, no del despliegue. Si no, una entrega que solo toca una pantalla del panel anunciaría cada vez 1750 direcciones como modificadas, que es una buena manera de acabar siendo ignorado. Cuando cambia la tabla de precios se notifican las dos rutas que renderizan precios, en nueve idiomas, que son 18 direcciones.
Lo que parecía un error de configuración
La primera notificación real después del despliegue, con la clave de producción ya puesta, devolvió 403.
El fichero de la clave devolvía 200 desde todos los sitios que probamos, incluso con un user agent de rastreador. Todos los ingredientes cuadraban. El paso siguiente natural es ir a mirar la configuración del borde, o la reescritura, o el orden de las fases, que es donde se va una hora.
Lo que hicimos en su lugar fue probar dos cosas más pequeñas. Una notificación de una sola URL devolvió 200 de inmediato. La misma notificación en bloque, un minuto después, también devolvió 200, en los dos endpoints.
Así que el 403 era un efecto de primer contacto en su lado, no un defecto en el nuestro. El servicio receptor todavía no había descargado ni cacheado el fichero de la clave en el momento en que llegó la primera petición en bloque.
Lo hemos dejado por escrito donde vive el manual de operaciones, porque el fallo está perfectamente diseñado para mandar a alguien de caza: el código de estado dice prohibido, que es una afirmación sobre permisos, y todo lo relativo a los permisos era correcto. Antes de investigar un fallo de autorización en una primera petición, repita la misma petición. No cuesta nada y es la hipótesis más barata de la lista.
Hay una nota relacionada para quien rote esa clave: el fichero se sirve con una hora de caché, así que una rotación necesita un purgado o una hora de paciencia, y durante esa ventana el mismo 403 significa algo completamente distinto.
Lo que dejaríamos por escrito
- Dos reglas correctas pueden producir un defecto en la costura que hay entre ellas. Ninguno de los dos dueños se equivoca y ninguno está mirando.
- El mismo identificador puede tener dos superficies con dos respuestas correctas. Dos funciones, no una.
- Una regla en un comentario no es un control. La nuestra estaba escrita con todo detalle y se medía contra la lista equivocada.
- Lo que existe y lo que responde son conjuntos distintos, y divergen justo cuando algo está oculto.
- Un rastreador solo encuentra defectos en páginas alcanzables. El nuestro no podía ver el tercer caso en absoluto.
- Una guarda encuentra el caso que todavía no se ha publicado. Ese es el motivo entero para escribir una en lugar de arreglar la línea.
- Dele a cada guarda una prueba que demuestre que está mirando. Cero enlaces muertos y cero enlaces se leen igual.
- Una cota inferior en un patrón puede ser un límite de seguridad. Ocho caracteres es lo que mantiene una reescritura lejos del fichero robots.
- Anuncie los cambios de contenido desde lo que sabe que el contenido ha cambiado, nunca desde el despliegue.
- Reintente antes de diagnosticar el fallo de una primera petición. Un prohibido que se convierte en un éxito un minuto después nunca fue una cuestión de permisos.
Para que quede claro qué es esto: el rastreo cubrió 1776 páginas contra producción y 1750 entradas de sitemap con 913 fechas de modificación, ninguna de ellas en el futuro, la corrección es una sola línea protegida por tres pruebas demostradas primero en rojo, la vía de notificación se ejercitó de extremo a extremo contra un endpoint simulado incluyendo cinco vías de fallo, y el barrido sobre una compilación de producción autónoma terminó en 96 en verde. Sin demostrar: en el momento en que se escribió la corrección estaba en verde sobre una compilación de producción local y todavía no se había confirmado en producción, y dos familias de rutas del rastreo no produjeron ninguna página porque sus registros están vacíos.
Del trabajo de plataforma en Provibr. Lo que una migración de facturación puede y no puede llevarse está en quince cosas que no pueden viajar, un despliegue cuyo paso de esquema nunca se ejecutó está en la migración que nunca se ejecutó, y otra comprobación que estaba en verde porque nunca llegó a su destino está en en verde porque no podía llegar hasta allí.