Módulo 6: Puertas de calidad — cobertura y umbrales que rompen el build
6. Cuándo la puerta ayuda (y cuándo estorba)
Descripción
Ya sabes montar puertas: de cobertura (lecciones 3 y 4) y de marcador (lección 5). Esta lección da el paso que separa a quien sabe montar puertas de quien decide cuáles poner: el juicio. Porque una puerta no es buena ni mala en abstracto —es buena o mala para un proyecto, con un umbral, sobre una métrica—. La misma puerta de cobertura al 80% que salva a un equipo de la erosión silenciosa puede, en otro contexto o con otro umbral, bloquear trabajo legítimo, empujar a trampas para cumplirla, y volverse el estorbo que todos aprenden a rodear. Saber cuándo una puerta ayuda y cuándo estorba es lo que evita los dos fracasos opuestos: no poner ninguna puerta (y dejar que la calidad se erosione) o poner puertas malas (y que el equipo las odie y las sabotee).
Al terminar vas a tener un criterio claro para decidir. Vas a saber cuándo una puerta ayuda: cuando frena una erosión que si no nadie notaría, cuando hace explícita una decisión que si no nadie tomaría, cuando protege algo que de verdad no puede romperse. Y cuándo estorba: cuando el umbral es un número aspiracional que bloquea trabajo sano, cuando la métrica no mide lo que importa, cuando la puerta castiga refactors legítimos. Vas a llevarte la regla del umbral honesto —ponlo donde estás y súbelo con trinquete, no un número redondo que quisieras tener— y la distinción de fondo que atraviesa todo el módulo: cobertura no es calidad; es una condición necesaria, no suficiente. Una puerta bien puesta cuida un piso mínimo; ninguna puerta garantiza que lo de arriba del piso valga algo.
Conexión con el módulo: esta lección es la bisagra entre el "cómo" (lecciones 3-5) y el "cuándo tiene sentido". Retoma la erosión de la lección 4 (el caso donde la puerta claramente ayuda) y anticipa la 7 (el caso donde el fetiche del 100% claramente estorba, empujando tautologías). Es la lección menos técnica y la más importante para el oficio: las herramientas las aprendiste; aquí aprendes a no usarlas mal. Sin este juicio, es fácil terminar con un CI lleno de puertas que nadie respeta —o sin ninguna, dejando la puerta abierta a la erosión—.
El guardia que revisa lo que importa, no el que revisa todo
Imagina dos guardias de seguridad en la entrada de un edificio. El primero es sensato: revisa que cada persona tenga su credencial, detiene a quien no la trae, y deja pasar con fluidez a quien sí. Su control protege —nadie entra sin permiso— sin estorbar a nadie legítimo. La gente lo respeta porque su regla tiene sentido: la credencial es exactamente lo que separa a quien debe entrar de quien no.
El segundo guardia es un desastre bien intencionado. Revisa la credencial, sí, pero también le mide la altura a cada persona, le pregunta su tipo de sangre, y exige que los zapatos estén lustrados. Sus reglas no separan a nadie peligroso de nadie legítimo —el tipo de sangre no dice si tienes permiso de entrar—; solo hacen la fila eterna. La gente empieza a odiarlo, y peor: aprende a rodearlo, entrando por la puerta de atrás para evitar el circo. El segundo guardia no solo estorba; destruye el control, porque enseña a todos a evadirlo. Una regla que estorba sin proteger no es neutra: es peor que no tener regla, porque entrena al equipo a saltarse los controles.
Una puerta de calidad es un guardia. Puede ser el primero —revisar lo que de verdad importa, proteger sin estorbar, ganarse el respeto— o el segundo —exigir cosas que no separan lo bueno de lo malo, hacer la fila eterna, y enseñar al equipo a rodear el control—. La diferencia no está en tener puerta o no; está en si la puerta revisa lo correcto con la exigencia correcta. Una puerta de cobertura al 80% sobre código de negocio crítico es el primer guardia. Un umbral del 100% que empuja a escribir tests falsos para cumplirlo es el segundo: la gente aprende a "rodearlo" con tautologías, y el control se vuelve teatro. Este es el juicio que la lección te enseña: ser el primer guardia, no el segundo.
Una puerta ayuda cuando revisa lo que de verdad importa con la exigencia correcta —protege sin estorbar, como el guardia que pide la credencial—. Estorba cuando exige lo que no separa lo bueno de lo malo —bloquea trabajo sano y enseña al equipo a rodearla, como el guardia que mide la altura—. Una puerta que estorba sin proteger es peor que ninguna: entrena a evadir los controles.
Cuándo la puerta ayuda
Hay tres situaciones donde una puerta paga con creces, y vale la pena reconocerlas porque son el argumento a favor de ponerlas.
Cuando frena una erosión que nadie notaría. Es el caso de la lección 4, y es el más fuerte. La cobertura de Reservo estaba en 91%; entró código sin tests y cayó a 83.53%, sin que ningún build se pusiera rojo. Sin una puerta —un trinquete—, esa erosión sigue push a push hasta que la mitad del código no está probado, y nadie decidió que así fuera: pasó por acumulación de descuidos. Una puerta convierte ese deslizamiento invisible en un rojo visible. Aquí la puerta ayuda inequívocamente, porque el problema que ataca —la erosión silenciosa— es real, común, y de otro modo invisible. Nadie va a notar a mano que la cobertura bajó dos puntos esta semana; la puerta sí.
Cuando hace explícita una decisión que si no nadie tomaría. Sin puerta, "mergear código sin tests" no es una decisión que alguien tome conscientemente —simplemente pasa, con prisa, un viernes—. Con una puerta, bajar la cobertura requiere una acción deliberada: o escribes el test, o bajas el umbral a propósito (y eso queda en el historial, visible, discutible). La puerta no prohíbe mergear código sin tests; obliga a que sea una elección, no un accidente. Convierte un descuido silencioso en una decisión que alguien tiene que tomar con nombre y apellido. Eso es valioso incluso cuando la decisión final es "sí, esta vez mergeamos sin test, por esta razón": al menos fue decidido, no deslizado.
Cuando protege algo que de verdad no puede romperse. Es el caso de la puerta smoke (lección 5). El descuento pro de Reservo cobra dinero real; si se rompe, un cliente paga mal. Una puerta smoke que exige que ese test pase antes de mergear protege exactamente lo que no puede llegar roto a producción. Aquí la puerta ayuda porque lo que vigila —lo crítico del negocio— justifica el control: el costo de la puerta (correr tres tests) es ínfimo frente al costo de lo que previene (cobrar mal a los clientes). La proporción entre lo que cuesta la puerta y lo que evita es lo que la hace valer la pena.
El hilo de los tres casos: la puerta ayuda cuando el problema que ataca es real y de otro modo invisible o accidental, y cuando lo que protege justifica el control. Erosión silenciosa, decisiones deslizadas, y lo crítico del negocio son los tres lugares donde una puerta gana su lugar.
Cuándo la puerta estorba
Y hay tres situaciones donde una puerta, aunque bien intencionada, hace más daño que bien.
Cuando el umbral es un número aspiracional que bloquea trabajo sano. Un equipo con cobertura real del 62% pone la puerta en 90 "porque queremos llegar ahí". Resultado: todo PR rompe el build, incluso los que mejoran la cobertura (de 62 a 65 sigue siendo < 90). El umbral no refleja dónde está el proyecto; refleja dónde alguien quisiera que estuviera, y castiga cada paso legítimo hacia esa meta. La gente empieza a saltarse la puerta (bajándola a mano, forzando el merge) porque cumplirla es imposible sin parar semanas a escribir tests de todo el código viejo. Un umbral aspiracional no motiva; frustra y se rodea. La cura es el umbral honesto (más abajo): ponlo en 62 y súbelo con trinquete.
Cuando la métrica no mide lo que importa. Una puerta de cobertura al 80% garantiza que el 80% de las líneas se ejecutan en los tests. No garantiza que esos tests verifiquen algo —un test que ejecuta la línea pero no afirma nada la "cubre" igual (lección 7)—. Si el equipo persigue el porcentaje como si fuera calidad, la puerta empuja justo hacia lo que no importa: subir un número que no mide lo que se cree que mide. Aquí la puerta estorba no porque la métrica sea inútil, sino porque se usa como si midiera algo que no mide. La cobertura mide ejecución, no verificación; confundirlas convierte la puerta en el segundo guardia, exigiendo el tipo de sangre en vez de la credencial.
Cuando castiga refactors legítimos. Un desarrollador borra código muerto —cien líneas que ya no se usan— y la cobertura baja, porque esas líneas, aunque muertas, estaban "cubiertas" por accidente y su desaparición cambia la aritmética; o sube el denominador de forma rara. Un trinquete rígido rompe el build por un cambio que mejoró el proyecto (menos código, más limpio). Una puerta que penaliza limpiar el código enseña al equipo a no limpiarlo —a acumular basura para no pelear con la puerta—. Aquí el juicio manda: una puerta debe tener holgura para los cambios que la bajan por buenas razones, o el equipo aprenderá a evitar esos cambios buenos.
El hilo de estos tres: la puerta estorba cuando exige lo imposible (umbral aspiracional), mide lo equivocado (ejecución confundida con calidad), o castiga lo bueno (refactors sanos). En los tres, la respuesta del equipo es la misma y la peor: aprender a rodear la puerta, que la vuelve teatro.
La regla del umbral honesto
De todo lo anterior sale una regla práctica que resuelve la mayoría de los casos: pon el umbral donde estás, no donde quisieras estar, y súbelo con trinquete.
Concretamente. Tu cobertura real es 84%. El umbral honesto es 84 (o un pelín menos, 83, para dar holgura a redondeos y refactors). No 90 "porque suena mejor", no 100 "porque es lo ideal". ¿Por qué donde estás? Porque un umbral en tu nivel actual cumple las dos cosas que quieres: no permite retroceder (cualquier caída lo cruza, es el trinquete de la lección 4) y no bloquea trabajo sano (cualquier PR que mantenga o mejore la cobertura pasa). Es el guardia que revisa la credencial: protege sin estorbar. Cuando la cobertura mejora a 88 de forma estable, subes el umbral a 88 —aprietas el trinquete—, y el nuevo nivel queda protegido. La cobertura sube gradualmente, cada mejora se consolida, y en ningún momento la puerta exige lo imposible ni permite el retroceso.
El contraste con el umbral aspiracional es total. El aspiracional (90 cuando estás en 62) bloquea todo y se rodea; el honesto (62 subiendo con trinquete) no bloquea nada legítimo y sube solo cuando de verdad mejoras. El aspiracional dice "llega a 90 ya"; el honesto dice "no empeores, y mejora a tu ritmo". El primero es una meta; el segundo es un mecanismo. Las metas motivan en un póster; los mecanismos protegen en el CI. Para la puerta, quieres el mecanismo.
Un número concreto para Reservo, cerrando el hilo del módulo: Reservo está en 91.03% con la suite completa. El umbral honesto es 91 —un trinquete en su nivel real—, que atrapa cualquier caída (como viste en la lección 4) sin bloquear ningún trabajo que mantenga la cobertura. No 100 (empujaría tautologías, lección 7), no 80 (dejaría pasar caídas hasta 80). El 91 es donde Reservo está, y por eso es el umbral que lo protege sin estorbarlo.
Cobertura no es calidad: la distinción de fondo
Bajo todo el juicio de esta lección hay una idea que conviene decir sin rodeos: la cobertura es una condición necesaria, no suficiente, de la calidad. Necesaria, porque código que no se ejecuta en ningún test jamás fue verificado —una cobertura baja es una alarma legítima—. No suficiente, porque código que sí se ejecuta puede estar verificado de mentira: un test que llama a la función pero no afirma nada la cubre al 100% sin comprobar que hace lo correcto.
Esto tiene una consecuencia directa para el juicio. Una puerta de cobertura es buena para lo que la cobertura sí mide —"¿hay código que ningún test toca?"— y peligrosa cuando se la trata como lo que no mide —"¿son buenos mis tests?"—. Usada como piso mínimo ("que no haya código completamente sin probar"), la puerta ayuda. Usada como calificación ("lleguemos al 95%, al 100%"), empuja al equipo a subir un número que no refleja lo que cree, y ahí empieza el daño de la lección 7. La misma herramienta, dos usos: uno sano (piso de seguridad) y uno tóxico (nota que perseguir).
La regla que se lleva bien con esto: usa la cobertura para encontrar el código sin probar, no para calificar el código probado. El reporte term-missing de la lección 3 —las líneas que ningún test toca— es la cobertura en su mejor uso: un mapa de dónde faltan pruebas. El porcentaje total como meta a maximizar es la cobertura en su peor uso. Una puerta que dice "no dejes código completamente sin probar" (un piso o trinquete honesto) es el primer guardia; una que dice "llega al 100% cueste lo que cueste" es el segundo. La lección 7 muestra, con una demo brutal, exactamente cómo el segundo uso pervierte los tests.
Errores comunes
Poner el umbral donde quisieras estar, no donde estás. Qué pasa: cobertura real 62%, puerta en 90, y ahora todo PR rompe el build —hasta los que mejoran la cobertura—. Por qué pasa: 90 "suena a proyecto serio" y parece motivador. Cómo detectarlo: si tu puerta rompe el build en PRs que suben la cobertura, el umbral está por encima de tu realidad. Cómo corregirlo: umbral honesto —ponlo en 62, súbelo con trinquete cuando mejores—. Un umbral que castiga las mejoras no motiva a mejorar; enseña a rodear la puerta. La meta aspiracional va en la conversación del equipo, no en el --cov-fail-under.
Tratar el porcentaje de cobertura como la calidad del código. Qué pasa: el equipo celebra llegar al 95% y concluye "nuestro código es excelente", cuando parte de ese 95% son tests que ejecutan sin verificar. Por qué pasa: es un número, y los números se sienten objetivos; es fácil olvidar qué mide exactamente. Cómo detectarlo: pregúntate "si rompo una función a propósito, ¿algún test se pone rojo?". Si la cobertura es alta pero la respuesta a veces es no, tu porcentaje mide ejecución, no verificación. Cómo corregirlo: usa la cobertura como piso ("que no haya código sin tocar"), no como nota ("qué tan bueno es el código"). La calidad de los tests se juzga por si atrapan bugs (lección 7), no por el porcentaje que reportan.
No poner ninguna puerta por miedo a que estorbe. Qué pasa: el equipo, escarmentado por una puerta mala del pasado, decide no poner ninguna, y la cobertura se erosiona sin freno. Por qué pasa: se confunde "esta puerta estaba mal calibrada" con "las puertas estorban". Cómo detectarlo: si tu cobertura baja mes a mes y no hay nada que lo frene, te falta la puerta que sí ayuda (el trinquete honesto). Cómo corregirlo: el problema no eran las puertas, era el umbral aspiracional. Una puerta honesta —trinquete en tu nivel actual— protege sin estorbar; es el primer guardia. No tirar al guardia bueno por el trauma del guardia malo. La respuesta a una puerta mala es una puerta bien calibrada, no la ausencia de puertas.
Ejercicios
Ejercicio 1 — Ayuda o estorba. Para cada puerta, di si probablemente ayuda o estorba, y por qué en una o dos frases. (a) Trinquete de cobertura en 91% sobre Reservo (cobertura real 91%). (b) Puerta de cobertura en 100% sobre un proyecto en 78%. (c) Puerta smoke que exige que el test del descuento pro pase antes de mergear. (d) Puerta que exige 85% de cobertura y rompe el build cuando alguien borra 200 líneas de código muerto (bajando la cobertura a 84%).
Ver solución
- (a) Ayuda. Umbral honesto: está en el nivel real (91), atrapa cualquier caída (trinquete) y no bloquea trabajo que mantenga la cobertura. El primer guardia: protege sin estorbar.
- (b) Estorba. Umbral aspiracional y encima el extremo: 100% sobre un proyecto en 78% bloquea todo PR y —peor— empuja a tautologías para cerrar el 22% restante (lección 7). El segundo guardia: exige lo imposible y enseña a rodearlo.
- (c) Ayuda. Protege algo que de verdad no puede romperse (el descuento pro cobra dinero real), con un costo ínfimo (correr un test). La proporción costo/beneficio es claramente favorable. Primer guardia.
- (d) Estorba (en ese momento). Castiga un refactor legítimo: borrar código muerto mejoró el proyecto, pero la puerta lo rompe por la aritmética de la cobertura. Es el caso donde el juicio debe intervenir —dar holgura, o bajar el umbral a 84 porque el 91 anterior incluía líneas muertas que ya no existen—. Una puerta que penaliza limpiar enseña a no limpiar.
El patrón: ayudan (a) y (c) —umbral honesto, protección proporcionada—; estorban (b) y (d) —umbral imposible, castigo a lo bueno—.
Ejercicio 2 — Arregla el umbral aspiracional. Un equipo tiene Reservo en 84% de cobertura y una puerta en --cov-fail-under=95 "porque queremos calidad". Cada PR rompe el build, la gente está frustrada, y dos personas ya forzaron merges saltándose la puerta. Diagnostica el problema y propón un arreglo concreto, con números.
Ver solución
Diagnóstico: es el umbral aspiracional del guardia malo. La cobertura real es 84% y la puerta exige 95%, así que todo PR que no suba 11 puntos de golpe rompe el build —incluidos los PRs que mejoran la cobertura (de 84 a 87 sigue siendo < 95)—. La puerta no separa el trabajo bueno del malo; bloquea todo por igual. Y ya apareció el síntoma clásico: la gente aprendió a rodearla (forzar merges), que es peor que no tener puerta, porque ahora los controles se saltan por costumbre.
Arreglo concreto: bajar el umbral al nivel honesto y convertirlo en trinquete.
- Pon
--cov-fail-under=84(o 83 para holgura). Ahora la puerta refleja dónde está el proyecto: no rompe el build en trabajo que mantenga la cobertura, y sí atrapa cualquier caída por debajo de 84. Deja de bloquear lo legítimo. - Sube el umbral con trinquete cuando la cobertura mejore. Cuando lleguen a 87 de forma estable,
--cov-fail-under=87. La cobertura sube gradualmente, cada mejora se consolida, sin exigir el salto imposible. - Deja el 95% como meta de conversación, no de CI. Si el equipo quiere llegar a 95, que sea un objetivo que discuten y planifican (escribir tests de los módulos flojos), no un muro que rompe cada PR. La meta motiva en la planeación; el trinquete protege en el CI.
Con esto, la puerta pasa de guardia odiado y evadido a guardia respetado: protege el nivel real (no se puede retroceder) sin bloquear a nadie que trabaje bien. Y desaparece el incentivo a forzar merges, porque cumplir la puerta vuelve a ser posible.
Ejercicio 3 — La distinción necesaria/suficiente. Un compañero dice: "Tenemos 92% de cobertura, así que nuestro código está 92% bien probado y es de alta calidad." Corrige la afirmación usando la distinción necesaria/suficiente, y da un ejemplo concreto de cómo 92% de cobertura puede convivir con tests que no verifican nada.
Ver solución
La afirmación confunde cobertura con calidad de los tests, que son cosas distintas. La corrección, con la distinción:
-
La cobertura es necesaria pero no suficiente. El 92% dice que el 92% de las líneas se ejecutan cuando corren los tests. Eso es necesario para la calidad —código que no se ejecuta jamás fue verificado— pero no suficiente: que una línea se ejecute no dice nada sobre si algún test afirma que hace lo correcto. "92% ejecutado" no es "92% verificado".
-
Ejemplo concreto de convivencia: imagina que Reservo tiene un test así para
booking_confirmed_message:def test_confirmed_message_runs(): msg = booking_confirmed_message(booking) assert msg is not NoneEste test ejecuta la función entera —la cubre al 100%, sube el porcentaje— pero solo afirma que el resultado "no es nulo". Si mañana alguien rompe el mensaje y devuelve
"texto totalmente equivocado", el test sigue pasando (el texto equivocado tampoco es nulo). La línea está "cubierta", el porcentaje se ve bien, y sin embargo la función está rota y ningún test lo nota. Ese es un test tautológico: aporta cobertura sin aportar verificación.
La afirmación correcta sería: "Tenemos 92% de cobertura, lo que significa que casi todo el código se ejecuta en los tests —bien, no hay grandes zonas sin tocar—. Cuánto de eso está de verdad verificado depende de si nuestros tests afirman el comportamiento correcto, que es otra pregunta que la cobertura no responde." La lección 7 muestra esta trampa en acción, ejecutada de verdad.
Resumen y siguiente paso
En esta lección diste el paso del "cómo" al "cuándo": el juicio para decidir qué puertas poner. Una puerta es como un guardia —puede revisar lo que importa y proteger sin estorbar (el primer guardia), o exigir lo que no separa lo bueno de lo malo, bloquear trabajo sano, y enseñar al equipo a rodearla (el segundo, que es peor que ninguna)—. Viste los tres casos donde una puerta ayuda —frena una erosión invisible, hace explícita una decisión deslizada, protege lo crítico del negocio— y los tres donde estorba —umbral aspiracional que bloquea trabajo sano, métrica confundida con calidad, castigo a refactors legítimos—.
Te llevas dos reglas de fondo. La del umbral honesto: ponlo donde estás, no donde quisieras estar, y súbelo con trinquete —el mecanismo que protege sin estorbar, frente a la meta aspiracional que frustra y se rodea—. Y la distinción de fondo: cobertura no es calidad; es una condición necesaria (código sin ejecutar jamás fue verificado) pero no suficiente (código ejecutado puede estar verificado de mentira). Usa la cobertura para encontrar el código sin probar, no para calificar el código probado.
Antes de avanzar deberías poder: distinguir una puerta que ayuda de una que estorba por si revisa lo correcto con la exigencia correcta; aplicar la regla del umbral honesto (nivel actual + trinquete) frente al aspiracional; explicar por qué una puerta que estorba es peor que ninguna; y articular por qué la cobertura es necesaria pero no suficiente para la calidad.
Lo que sigue, en la lección 7, es la demostración brutal del peor uso de una puerta: el 100% como fetiche. Cuando el umbral se vuelve una meta a maximizar cueste lo que cueste, el incentivo se pervierte y el equipo escribe tests tautológicos —que ejecutan el código para subir el número sin verificar nada—. Vas a ver, ejecutado de verdad, un test que sube la cobertura a verde y que sigue pasando cuando se rompe el código a propósito: el "verde que no verifica" de la guía de fundamentos, convertido ahora en el producto directo de una puerta mal entendida.
Recursos
- Coverage.py: qué mide (y qué no) la cobertura — la introducción oficial, con la aclaración de que la cobertura mide qué código se ejecutó, no si se verificó. La base de la distinción necesaria/suficiente de esta lección.
pytest-cov:--cov-fail-undercomo piso, no como meta — la bandera del umbral; el número que le pones es la decisión de juicio central. Repasa cómo un umbral honesto (nivel actual) se distingue de uno aspiracional.- Coverage.py:
precisionpara trinquetes finos — cómo fijar decimales del umbral para que un trinquete no rompa el build por un redondeo, útil cuando das holgura a refactors legítimos. - Acerca de la protección de rama y los checks requeridos — GitHub — cómo una puerta se vuelve condición de merge de verdad; el mecanismo que hace que un umbral honesto proteja la rama sin que nadie tenga que vigilarla.