Saltar al contenido principal
Allways

10 min de lectura

Cómo se comprueba que un gate puede fallar

Un gate que nunca ha dado rojo no está probado: está sin estrenar. El método para validarlo contra el defecto vivo, y las cuatro reglas que salen de aplicarlo.

Allways Startup

Un gate automático que salta sin motivo es molesto y barato. Alguien lo mira, discute cinco minutos, lo afina y sigue. El modo de fallo caro es el otro, el que casi nadie audita: el gate que da verde sobre un defecto que está vivo, servido y a la vista de cualquiera que abra la página.

Y es caro porque es invisible por construcción. Un gate que grita se arregla; un gate que calla se cita. Cada vez que alguien lo ve en verde, deja de mirar la cosa que el gate decía cubrir, y el hueco se hace más grande justo donde nadie va a volver a pasar.

Hay un método para saber si un gate sirve, y es el que casi nadie aplica: estrenarlo contra el defecto todavía vivo, y contar cuántos encuentra. Verde sobre código ya limpio no es evidencia de nada: no hay nada que encontrar, así que un detector roto y un detector perfecto dan exactamente el mismo resultado.

Lo que sigue son cinco casos donde el método encontró un gate que medía una proxy: algo correlacionado con la respuesta, más fácil de calcular, y que en el momento de la verdad no era la pregunta. Los cinco fallaban hacia el mismo lado, y de los cinco salieron cuatro reglas que hoy no se negocian.

El probe que preguntaba si el título estaba a la vista, no si estaba bien colocado

Un probe recorría los 6 enlaces de sección de una página y comprobaba, para cada uno, que el destino quedara dentro del viewport después del salto. Verde en los seis. El booleano era verdad.

El día que en lugar del booleano medimos el número, apareció esto: el destino aterrizaba a 272 píxeles del borde superior de la ventana, con una barra fija de 80 píxeles encima. Restá y vas a ver el problema. Entre el borde inferior de la barra y el título al que acababas de saltar quedaba, en vacío, más del doble del alto de la barra entera. Hacías clic en un enlace del índice, el navegador saltaba, y el título aparecía flotando en mitad de la pantalla con una franja de negro arriba que no significaba nada.

"Está en el viewport" era cierto. Y no era la pregunta.

La causa fueron tres compensaciones de scroll sumándose sin que ninguna supiera de las otras: 96 píxeles declarados en el elemento destino, 96 más en la raíz del documento, y 80 dentro de la librería de scroll suave. 96 más 96 más 80 da exactamente 272. La aritmética cerraba perfecto y nadie la había hecho nunca, porque el gate decía verde.

Lo que más costó digerir no fue el número. Fue que había un comentario en el CSS afirmando de forma explícita que ahí no existía doble compensación, "porque la librería ignora el scroll-padding". La librería no lo ignoraba: lo resta primero y después suma su propio offset. No sabemos si ese comentario fue verdad alguna vez. Sabemos que nadie volvió a comprobarlo, y que el comentario terminó siendo el motivo por el que nadie lo comprobaba.

Un comentario que afirma algo verificable y que nadie verifica es un gate apagado con forma de documentación. Tiene toda la autoridad de una comprobación y ninguna de sus propiedades.

El arreglo tiene dos mitades. La primera: una sola fuente de compensación, derivada del alto real de la barra, en lugar de tres valores independientes que se pisan.

:root {
  --alto-barra: 80px;
  --compensacion-ancla: var(--alto-barra);
}
css · Fragmento de código del artículo

La segunda: el probe ahora mide el número de píxeles, no un booleano, y lo mide por las dos vías, con JavaScript y sin él. Si las dos cifras no coinciden, la página se ve distinta según si el JavaScript llegó a cargar, y eso es un defecto por derecho propio aunque cada cifra por separado parezca razonable.

El limpiador de comentarios que se tragaba media línea de marcado

Varios de nuestros gates de texto necesitan quitar los comentarios del código antes de buscar. La razón es específica de cómo trabajamos: acá los comentarios documentan los defectos por su nombre. Cuando se retira una afirmación de una página, queda un comentario explicando qué se retiró y por qué. Así que un gate que busca esa afirmación para asegurarse de que no volvió encuentra, invariablemente, el comentario que cuenta que se fue.

De ahí el limpiador. Y el limpiador tenía un fallo de una línea.

Trataba // como principio de comentario siempre. En JSX el texto visible no va entre comillas, va suelto en el marcado, así que un párrafo que contenga una dirección web lleva dos barras desnudas en mitad de la frase.

// El limpiador ingenuo: todo lo que siga a // es comentario.
function quitarComentarios(src: string): string {
  return src.replace(/\/\/.*$/gm, '')
}

// Entrada: <p>Escribinos a https://dominio.ejemplo/contacto antes del jueves</p>
// Salida:  <p>Escribinos a
// El gate busca "antes del jueves", no lo encuentra, y reporta PASS.
ts · Fragmento de código del artículo

Todo lo que viniera después de esas dos barras, en esa línea, dejaba de existir para el gate. No lo marcaba como sospechoso: lo borraba. El defecto seguía publicado y el informe decía que estaba limpio.

Lo importante no es el fallo, que es trivial. Es la dirección del fallo. Una transformación de la entrada puede equivocarse en dos sentidos: borrando de más, y entonces el gate deja de ver cosas que están ahí, o borrando de menos, y entonces el gate se queja de cosas que en realidad eran comentarios. Los dos son errores. No cuestan lo mismo.

Cuando una transformación puede equivocarse en las dos direcciones, elegí la dirección que produce falsos positivos.

Un falso positivo sale en rojo, alguien lo mira, refunfuña y lo descarta en un minuto. Un falso negativo sale en verde y no lo mira nadie, nunca. Esa asimetría tiene que estar en el diseño de la herramienta de forma explícita, no librada a cómo salió la expresión regular.

El arreglo tampoco fue una expresión regular más astuta. Podés escribir una guarda para el caso del esquema de URL y vas a tapar ese agujero concreto, no la clase:

function quitarComentarios(src: string): string {
  // Dos barras precedidas de ':' son un esquema de URL, no un comentario.
  return src.replace(/(^|[^:])\/\/.*$/gm, '$1')
}
ts · Fragmento de código del artículo

Lo que de verdad cambió la situación fue darle al limpiador su propio banco de casos: 13 entradas con su salida esperada, que se ejecutan antes de que el gate mire un solo fichero del proyecto. Si alguna falla, el gate se niega a analizar nada y sale en rojo. Un gate que no se valida a sí mismo no puede validar nada.

El gate escrito contra el ejemplar, y no contra la especie

Se retiró de una página un dato estructurado que no correspondía publicar. Para que no volviera por descuido, se escribió un gate. El gate buscaba la forma exacta de lo que se acababa de matar: la misma cadena, el mismo tipo, la misma página.

Meses después alguien miró la página de al lado. 24 instancias del mismo tipo de dato, vivas, servidas, presentes todo ese tiempo. El gate seguía en verde. Nunca las vio porque nunca las buscó: había sido escrito contra un ejemplar, no contra la especie.

// Contra el ejemplar que motivó el gate:
const contraElEjemplar = /"@type"\s*:\s*"AggregateRating"/

// Contra la clase del defecto:
const contraLaClase = /"@type"\s*:\s*"(AggregateRating|Rating|Review|Offer)"/
ts · Fragmento de código del artículo

La pregunta que faltaba ocupa una línea, y hay que escribirla literalmente, no pensarla: ¿qué otras formas tiene este defecto? Y enumerarlas. Si la lista te queda con una sola entrada, no terminaste de pensar; terminaste de acordarte del caso que te trajo hasta acá.

La variante más traicionera de este error es la numérica. Cuando un gate persigue una cifra, es facilísimo escribirlo como una búsqueda de dígitos y quedarte con la sensación de que el asunto quedó cubierto. No queda. Hay que buscar además la forma que dice lo mismo sin cifra, que es justo la que ningún buscador de dígitos encuentra jamás.

Nos pasó con una página que afirmaba cobertura total de sectores sin escribir una sola cifra. Esa frase es más fuerte que cualquier número que pudiéramos haber puesto, y sobrevivió a todos los barridos por la única razón de no llevar un dígito adentro.

Y hay un detalle que cierra el caso: cuando escribimos este artículo, el gate volvió a saltar. La frase original, citada aquí como ejemplo, la cazó igual que había que cazarla en su día. Tenía razón: un buscador que lee esta página encuentra la afirmación, no el contexto que la desmiente. Así que está descrita, no reproducida.

El control negativo que sólo podía dar cero

Un control negativo comprobaba que en cierto fichero no quedara ningún identificador con un prefijo determinado. Corrió, dio cero, y cero era el resultado correcto. El gate quedó instalado y contento.

El problema es que en ese fichero no había ningún identificador escrito de forma literal, de ninguna clase. Todos se construían en tiempo de ejecución a partir de un dato. El control habría dado cero igual si el identificador emitido hubiera sido cualquier otra cosa, incluida exactamente la que decía prohibir. No estaba comprobando la ausencia del defecto: estaba comprobando la ausencia de literales, que es otra cosa y que era verdad por construcción.

Un gate que sólo puede dar verde no es un gate, es una línea de más en el informe.

De ahí salió la regla que hoy no se discute: un gate nuevo se corre contra el defecto que dice detectar, todavía vivo, y se cuenta cuántos encuentra. Verde sobre código ya limpio no es evidencia de nada, porque no hay manera de distinguir "no hay defectos" de "no sé mirar".

En la práctica es barato. Recuperás con el control de versiones la versión anterior, la que todavía tenía el problema, apuntás el gate ahí y contás. Si sabés cuántos hay y los encuentra todos, el gate sirve. Si encuentra menos, el gate está mal, y además ya sabés en qué dirección está mal.

La clase que se llamaba degradado y pintaba un color plano

Una regla de accesibilidad señaló una clase CSS llamada .text-gradient, y el razonamiento con el que la señalaba era impecable: un degradado aplicado sobre las letras no tiene un ratio de contraste, tiene un rango, y el extremo oscuro de ese rango puede quedar por debajo del mínimo. Todo correcto.

La clase no era un degradado. Un refactor anterior le había quitado el degradado y le había dejado un color plano. Lo que no le quitó fue el nombre. Medido contra el fondo real sobre el que se pinta, ese color da 7,04:1, que pasa AA y también AAA.

La regla no se equivocó razonando. Heredó la mentira del nombre: leyó el selector, dedujo la técnica y aplicó un argumento correcto al objeto equivocado.

El nombre de una clase es una afirmación. El cuerpo de la regla CSS es la evidencia.

Cuando el nombre ya no describe lo que hace, el arreglo barato es renombrar la clase, no volver a discutir el efecto. Renombrar cuesta un minuto. Rediscutir el efecto cuesta una tarde, y la próxima vez que alguien lea el selector va a costar otra igual.

El corolario del corolario no se queda en el CSS: medí el token, no lo supongas. Un valor declarado en un sistema de diseño es comprobable en segundos, y suponer su contraste a partir de cómo se llama es el mismo error con otra ropa.

Las cuatro reglas que quedaron escritas

De los cinco casos salen cuatro reglas, y hoy no se negocian.

Todo gate se escribe contra la clase del defecto, no contra el ejemplar que lo motivó. El ejemplar es lo que te hizo abrir el editor. No es lo que estás cazando.

Todo gate llega con dos controles. Uno positivo, con la forma del defecto real, para demostrar que sabe encontrarlo. Uno negativo, con una forma legítima que se le parece, para demostrar que no la caza. Un gate sin control positivo no es un gate: es decoración con exit code.

Ningún gate puede fallar hacia el verde. Si le falta un dato, si no pudo cargar la página, si la transformación previa reventó, sale en rojo. La ausencia de evidencia no se reporta nunca como evidencia de ausencia.

Y si el gate transforma la entrada antes de buscar, si limpia comentarios, normaliza espacios o quita marcado, esa transformación se prueba por separado, con su propio banco de casos, antes de que el gate opine sobre nada.

Ninguna de las cuatro se deduce de la teoría: las cuatro salen de mirar qué tendría que haber pasado para que un verde fuera rojo, y de comprobarlo con el defecto delante. Si te interesa cómo se traduce esto al trabajo del día a día, lo contamos en cómo trabajamos.

Y ésa es la pregunta que ordena todo lo demás. Ante un verde, la primera pregunta no es si está bien: es bajo qué condición concreta esto habría salido rojo. Si no hay respuesta, no hay comprobación: hay un exit code.