El email del usuario fue corto: “No me llega el SMS para resetear mi contraseña”. Lo escribió tres veces. La última con mayúsculas.
Yo había escrito la suite de recuperación de contraseña hacía 8 meses. 12 tests, todos verdes en CI durante semanas. Monitoreo de la URL del endpoint, alertas de latencia, dashboard verde en Grafana.
Pero el SMS no le llegaba a ese usuario. Y a otros que también escribieron después.
Lo que pasó después es lo que quiero contar — porque la lección no es sobre el bug. La lección es sobre lo que estaba haciendo MAL en cómo armaba la suite.
La realización: el sesgo de “lo que ya testeo es lo que existe”
12 tests no eran pocos. Cubrían el happy path. Cubrían que el SMS se enviara. Cubrían que el código aceptado redirigiera a la pantalla de nueva contraseña.
Lo que NO cubrían — y esto es lo que me dolió darme cuenta — eran las 50 cosas que pueden romperse ENTRE “el SMS se envió” y “el usuario lo recibe e ingresa el código bien”.
El sesgo es el siguiente: yo había escrito tests para lo que YO me imaginaba que podía fallar. Lo que mi experiencia de QA con 15 años en el oficio me decía. Pero los usuarios reales tienen contextos que no veo desde mi escritorio: planes de teléfono con SMS limitados, números cambiados que nunca actualizaron en el perfil, providers regionales con latencia variable, lockouts por intentos previos olvidados.
Si UN usuario me reportó esto, ¿cuántos más estaban abandonando el flujo sin escribir? ¿Cuántos otros bugs vivían en mi suite, invisibles, esperando a que alguien lo suficientemente molesto se tomara el trabajo de escribirme?
Esa pregunta fue la que me hizo cambiar de estrategia. No quería sentarme a pensar QUÉ más podía fallar — eso ya lo había hecho mal una vez. Quería que algo mirara el flujo sin cargar mis sesgos.
El experimento con Claude Code
Abrí Claude Code en el terminal y le pasé esto, literal:
“Investiga el flujo a ver qué puede estar fallando y por qué no llegan los SMS de recuperar contraseña al usuario.”
Lo que destaca: es corto. Una sola frase. No estructuré nada elaborado, no le pasé un test plan, no le dije qué herramienta usar. Solo le pedí: investiga, no resuelvas.
Antes de mandar esa frase le di tres cosas concretas:
- La URL del portal de pruebas
- Un usuario de prueba
- La contraseña asociada
Sin esas tres cosas, Claude Code se queda en suposiciones genéricas. CON esas tres cosas, puede recorrer el flujo real, mirar las llamadas que hace la app, identificar dónde concretamente algo se rompe.
Dos cosas conscientes en cómo formulé la pregunta:
- No le pedí tests. Le pedí que investigara. Tests es output; investigación es proceso. Quería su mirada, no su código.
- No le di mi hipótesis del problema. Cualquier QA con 15 años en el oficio tiene sospechas inmediatas cuando lee “no llega el SMS”: ¿es el provider? ¿es el rate limit? ¿es la config? Hubiera sido tentador escribirle “fíjate si es alguna de estas tres”. No lo hice. Quería que pensara desde cero, sin contaminarse con mis sesgos previos — porque mis sesgos previos eran exactamente los que me habían hecho perderme el bug en primer lugar.
Claude Code exploró el portal durante 15 minutos. Probó el flujo normal, probó variaciones de inputs, probó cosas que sonaban tontas como “qué pasa si pongo un número de teléfono con un emoji”. Volvió con una lista de 32 escenarios agrupados en categorías. De esos 32, 24 no estaban en mi suite.
Hay una diferencia enorme entre “Claude, escríbeme los tests para este feature” e “Investiga el flujo a ver qué está fallando”. La primera te da código. La segunda te da perspectiva — que es lo único que una IA puede aportarle a un QA senior. Código de tests sabes escribir tú. Ángulos que se te escapan, no siempre.
Las 8 categorías que salieron
Los 24 escenarios nuevos se agruparon naturalmente en 3 grupos de 8 categorías totales. Te las listo no por completitud — sino para que veas el TIPO de gaps que viven en suites “completas” como la mía.
Fallos por timing del happy path
Lo que pasa cuando todo parece estar “bien” pero el timing rompe.
- Timeout del SMS sin llegar. ¿Qué hace la UI si pasaron 90 segundos y nada? ¿Hay reintento automático? ¿Hay mensaje claro al usuario? Mi suite asumía que el SMS siempre llegaba en menos de 5 segundos.
- Expiración del código. El código vive 10 minutos. ¿Qué pasa al minuto 11? ¿Y a las 2 horas? ¿La invalidación es del frontend o del backend? Bug sutil que salió: la UI mostraba “código válido” hasta el siguiente refresh, aunque el backend ya lo había rechazado.
- Reuso del mismo código. ¿Se puede usar dos veces si el primer intento falló por un typo? Spoiler: sí se podía. Eso es un agujero de seguridad pequeño pero real.
Fallos del proveedor SMS externo
Tu proveedor no es código tuyo, pero los bugs que vienen de él los reporta tu usuario igual.
- Caída del proveedor SMS. Si Twilio devuelve 5xx, ¿el usuario ve “intentá más tarde” o “código enviado” (mentira)? Spoiler: la app le decía “código enviado” sin verificar la respuesta del provider.
- Rate limit del proveedor. Si la app supera el cap de SMS/segundo, ¿qué error sale al usuario? ¿Hay backoff automático? ¿O reintenta inmediatamente y agrava el rate limit?
- Lockout por intentos. Si el usuario pide 10 SMS en 5 minutos, ¿se le bloquea? ¿Por cuánto tiempo? ¿Cómo se entera él de que está bloqueado?
Fallos por contextos del usuario real
Lo que mi escritorio no me deja ver pero los usuarios viven todos los días.
- Número telefónico cambiado. El usuario actualizó su número en el perfil hace 6 meses pero el sistema legacy de auth usa el número viejo. ¿El reset usa cuál de los dos? ¿Hay verificación del nuevo número antes de aceptarlo?
- Formato de número internacional. Usuarios con +595 (Paraguay), +53 (Cuba), +1 (US). ¿La app maneja todos los códigos país? ¿Tira error claro si el formato es ambiguo, o lo acepta y manda al vacío?
- Brute force al endpoint de validación del código. El código es de 6 dígitos = 1 millón de combinaciones. ¿Hay rate limit en el ENDPOINT del backend (no solo en el usuario)? ¿Bloqueo después de N intentos fallidos? Sin esto, un atacante podría barrer las combinaciones desde un script.
Más una categoría bonus que salió y que ni había considerado:
- Flujo alternativo por email. Algunos usuarios tienen reset configurado por SMS O por email. Si el SMS está caído, ¿el flujo email sigue funcionando? ¿Se notifica al usuario que tiene esa alternativa, o se queda mirando la pantalla del SMS que no llega?
8 categorías. 24 tests nuevos automatizables con Playwright + un mock del provider SMS. Tres casos quedaron manuales porque automatizarlos requería infraestructura de mocks que no justificaba el ROI para la frecuencia esperada del escenario.
Lo que NO hizo Claude Code — lo que SÍ hice yo
Acá está la parte que me importa que no se pierda. Si esta historia termina en “Claude Code generó 24 tests y los corrí en CI”, esto sería un hilo más de LinkedIn de los que abundan sobre IA. No es eso.
Lo que Claude Code no hizo:
- No decidió qué automatizar vs qué dejar manual. Eso lo hice yo, basado en frecuencia esperada del escenario, costo de mantenimiento del test, y criticidad del path. Tres casos quedaron como pruebas manuales mensuales porque automatizarlos requería infraestructura de mock SMS que no justificaba el ROI.
- No descartó tests innecesarios. Claude Code tira ideas, no prioriza. Yo descarté 8 de los 32 escenarios que sugirió: algunos eran tan improbables que el costo de mantener el test era mayor que el valor de cazar el bug, otros ya estaban cubiertos indirectamente por tests existentes.
- No conoció mi contexto. Claude Code no sabía que en este proyecto hay un SLA del proveedor SMS de 99.5% y que latencias arriba de 30s son aceptables por contrato. Eso lo aporté yo — y por eso descarté el test “alerta si latencia SMS > 5s” que había propuesto. Se hubiera disparado falsos positivos toda la semana.
- No corrigió el bug original. El bug del usuario que arrancó todo era una config del Twilio account (límite de SMS/día excedido por crecimiento de usuarios). Eso lo identificamos investigando los logs reales del proveedor, no analizando el flujo. Claude Code me ayudó a expandir la suite preventivamente; el bug en sí lo resolvió el equipo de plataforma con un cambio de plan.
La diferencia entre usar IA bien vs usar IA mal en QA está exactamente acá. Sin esta curaduría humana, terminas con suite inflada de 200 tests donde nadie sabe cuáles importan y todos pesan igual en el CI.
Antes de escribir el primer test de un feature, abro Claude Code (o ChatGPT, o el LLM que tenga a mano) y le digo: “Acá tienes el feature [link o spec]. Investiga qué puede fallar y dime QUÉ probablemente NO estoy considerando.” No le pido tests. Le pido ángulos. Después yo decido cuáles cubrir, con qué herramienta, y a qué frecuencia.
Cierre — qué cambió en mí
Cambié algo concreto en cómo arranco a testear cualquier flujo nuevo desde ese día. Y no es “usar más IA”. Es cómo la uso.
La IA no me hizo mejor QA. Me hizo MÁS difícil engañarme a mí misma sobre lo que mi suite cubre. Eso es valor distinto y vale más que cualquier herramienta nueva que aparezca el mes que viene.
El bug del SMS no me enseñó a testear recuperación de contraseña — eso ya lo sabía hacer. Me enseñó que mi sesgo de “lo que ya testeo es lo que existe” tenía 50 huecos que no veía. Tener algo que mire el flujo sin cargar mis sesgos — algo que destape los huecos antes de que un usuario los reporte — es la diferencia entre QA reactivo y QA preventivo. Y ese “algo”, hoy, puede ser una IA bien dirigida.
Escríbeme. Leo todas las respuestas, y los bloqueos reales me marcan la prioridad para las próximas piezas del blog. Tu pregunta puede destrabarle el camino a 100 QAs más.