En mi máquina funciona: guía de campo de los bugs no reproducibles

reportes de bugs

Un usuario reporta que la página se rompe. Sigues sus pasos al pie de la letra. Todo funciona. Cierras el ticket como no reproducible y, tres semanas después, cuatro personas más reportan lo mismo.

«En mi máquina funciona» se trata como un chiste, pero es una afirmación precisa y útil: el bug depende de alguna diferencia entre tu entorno y el suyo. Eso convierte una pregunta sin respuesta en una que se puede resolver. No «¿por qué este bug es tan raro?», sino «¿qué es distinto entre estas dos máquinas?».

No hay tantas diferencias candidatas. En las aplicaciones de navegador, prácticamente todas caen en ocho categorías, y cada una deja una huella reconocible.

1. Estado

La causa más común, con diferencia. Tú reproduces desde una carga limpia. El usuario lleva dos horas en esa pestaña.

Las aplicaciones de una sola página que viven mucho tiempo acumulan cosas: entradas obsoletas en una caché del cliente, un websocket que se reconectó con otra sesión, un store modificado por una ruta de código que el usuario recorrió hace una hora, un token de acceso que se renovó a mitad de camino. Tu carga recién hecha no tiene nada de ese historial.

Huella: funciona después de recargar. Si quien reporta dice «al recargar se arregla», casi seguro estás aquí, y la reproducción que necesitas no son sus pasos, sino sus pasos después de veinte minutos de uso que no tiene nada que ver.

Cómo detectarlo: la lista ordenada de lo que el usuario hizo realmente en la sesión, no de lo que le pareció relevante. Eso es lo que te da un registro de acciones y lo que un campo de «pasos para reproducir» nunca te dará, porque el paso relevante es el que no se le ocurrió mencionar.

2. Tiempos

En tu máquina todo es rápido. API local, caché caliente, disco rápido. En un teléfono, dentro de un tren, una petición que a ti te lleva 40 ms tarda 4 segundos, y una condición de carrera que en tu máquina nunca pierde, en la suya pierde siempre.

Huella: intermitente. «Pasa más o menos una de cada cinco veces». Intermitente casi siempre significa tiempos, y significa que las dos operaciones que compiten duran lo bastante parecido como para que la variación normal cambie quién gana.

Cómo detectarlo: los tiempos de red. Mira qué estaba en curso a la vez y cuánto tardó cada petición. La forma clásica son dos peticiones lanzadas juntas en las que el código asumía que la primera terminaría primero.

Cómo reproducirlo: limita la velocidad. La limitación de red y de CPU de DevTools convierte muchísimos bugs «no reproducibles» en «se reproduce siempre». Son los cinco minutos más valiosos de todo este post.

3. Entorno

Navegador, versión, sistema operativo y, lo más infravalorado, el tamaño del viewport.

Tú pruebas a 1440px en un Chrome de escritorio. El usuario está a 390px en Safari de iOS, o a 1280px con el navegador al 125% de zoom, que no equivale a 1024px y rompe maquetaciones por esa diferencia. Safari, en concreto, sigue yendo por su lado en el parseo de fechas, en los backdrop filters, en el comportamiento del scroll y en cómo trata el almacenamiento en navegación privada.

Huella: constante para quien lo reporta, nunca para ti. Se reproduce el 100% de las veces en su máquina.

Cómo detectarlo: navegador, sistema operativo y viewport registrados automáticamente con el reporte. Es la categoría con la mayor proporción entre «trivial de arreglar una vez que lo sabes» e «imposible de adivinar», y justo por eso preguntarle a un usuario qué versión del navegador usa es una manera tan mala de gastar una ida y vuelta con soporte.

4. Identidad y permisos

Tú has iniciado sesión como admin, en tu propia cuenta y en tu propia organización. El usuario es un miembro de solo lectura en un equipo de cuarenta personas, con un plan que tiene un feature flag desactivado.

Los bugs que dependen del rol se detectan sistemáticamente menos, porque quien desarrolla casi nunca navega por su propio producto como el usuario con menos permisos.

Huella: se reproduce para un tipo de usuario y no para otro. A menudo lo reportan a la vez varias personas que comparten rol, y eso hace que parezca una regresión repentina cuando lleva meses roto.

Cómo detectarlo: adjunta al reporte el rol del usuario, su plan y el estado de los flags. Cualquier SDK decente acepta contexto propio (en BugDump son identify() para saber quién es y setContext() para cualquier estado), y el rol y el plan son los dos campos que vale la pena adjuntar antes que ningún otro.

5. Datos

Su cuenta tiene un registro con un emoji en el nombre, o un null donde tú asumías un string, o 4000 filas donde tus fixtures tienen 12, o una moneda sin decimales, o una fecha de 1970 porque una importación salió mal.

Huella: se reproduce con un registro concreto y en ningún otro sitio. Quien reporta suele decir «solo me pasa con este proyecto», y eso es el diagnóstico completo servido en bandeja.

Cómo detectarlo: el ID del registro y la respuesta de la API que falla. Un cuerpo de respuesta con el valor problemático dentro resuelve esta categoría al instante. En buena parte por eso los cuerpos de respuesta son tan útiles, y por eso hay que tratarlos con cuidado: el mismo campo que contiene la fecha mal formada contiene también todo lo demás sobre el usuario.

6. Extensiones y scripts inyectados

Los bloqueadores de anuncios bloquean tu endpoint de analítica, y tu código no contemplaba que esa petición fallara. Los gestores de contraseñas inyectan DOM en tu formulario y rompen el manejo de eventos. Las extensiones de traducción reescriben nodos de texto y hacen añicos la reconciliación de React. Los proxies corporativos reescriben respuestas.

Huella: nadie puede reproducirlo, ni siquiera otros usuarios, y la consola muestra una petición que falló sin código de estado, o un error del DOM que apunta a un elemento que nunca renderizaste.

Cómo detectarlo: las peticiones fallidas sin respuesta son la señal. También lo es una repetición de sesión que muestra un DOM que tu aplicación no produce.

7. La propia red

Portales cautivos, interceptación TLS corporativa, fallos de DNS, un nodo de CDN que sirve un bundle obsoleto a una región, un despliegue a medias en el que el HTML es nuevo y el chunk al que hace referencia ya no existe.

Huella: agrupado en el espacio o en el tiempo. Varios reportes en un intervalo corto, o todos desde un mismo país.

Cómo detectarlo: las marcas de tiempo y la URL del recurso que falla. Un ChunkLoadError en la consola es esta categoría casi por definición, y significa que tu estrategia de despliegue borra los chunks antiguos antes de que las pestañas antiguas terminen de usarlos.

8. La hora

Zonas horarias, cambios de horario de verano, cambios de mes, formatos de fecha de cada idioma y la máquina cuyo reloj va once minutos desfasado, que basta para que un token parezca caducado nada más llegar.

Huella: se reproduce solo a ciertas horas, o solo para usuarios de ciertas regiones, o solo cerca de fin de mes.

Cómo detectarlo: el desfase horario y la hora local de quien reporta junto a la marca de tiempo del servidor. Es barato de registrar y resuelve esta categoría de un vistazo.

El método

Ante un reporte que no puedes reproducir, lo útil no es esforzarse más. Es clasificarlo en una de las ocho categorías anteriores, y cada una tiene una pregunta que la identifica rápido:

  • ¿Se arregla al recargar? → estado

  • ¿Es intermitente? → tiempos

  • ¿Le pasa siempre, el 100% de las veces? → entorno

  • ¿Comparte rol o plan con otras personas que lo reportaron? → identidad

  • ¿Es un registro concreto? → datos

  • ¿Falló una petición sin código de estado? → extensiones o red

  • ¿Se agrupa en el tiempo o en una zona geográfica? → red

  • ¿Solo a ciertas horas? → hora

Fíjate en que cada una de estas preguntas se responde con datos, no con una conversación. Esa es la razón real de que «no reproducible» sea tan habitual: las pruebas que clasificarían el bug en su categoría estaban en el navegador del usuario en el momento en que falló, y nadie las capturó.

Captura la diferencia, no la descripción

Vale la pena sistematizar esto porque los bugs no reproducibles no son casos límite raros. Son los que se quedan abiertos. Los bugs reproducibles se arreglan esa misma semana. El backlog está hecho del otro tipo, y el otro tipo es casi por completo un problema de datos.

Un reporte que llega con el navegador, el sistema operativo, el viewport, la zona horaria, el rol del usuario, el historial de la sesión, la consola y el registro de red no necesita que nadie adivine su categoría: la diferencia entre su máquina y la tuya está escrita en el reporte. Eso es lo que BugDump captura automáticamente con una sola etiqueta script, así que las ocho preguntas de arriba quedan respondidas antes de que nadie tenga que hacer ninguna.

Lo más escaso y más valioso de esta lista es la segunda persona que reporta el bug. Un reporte no reproducible es un misterio; tres, con tres entornos distintos adjuntos, son un experimento controlado: las variables que comparten son la causa, y las que no, ruido. Agrupar los reportes duplicados en una sola tarea no es poner orden. Justo en los bugs que no puedes reproducir, es el diagnóstico.

Te puede interesar