14 puntos por GN⁺ 2025-11-04 | 2 comentarios | Compartir por WhatsApp
  • El concepto de búsqueda binaria (binary search) no solo se usa en preguntas de entrevista, sino también en una herramienta de desarrollo real como Git
  • En un entorno de monorepo a gran escala, cuando las pruebas fallan de repente, puede darse una situación en la que sea difícil rastrear la causa solo con los logs
  • Un colega marcó un commit bueno y uno malo, y ejecutó una búsqueda automática con git bisect, encontrando con precisión el commit problemático donde comenzó el bug
  • En cada etapa se ejecuta un script para clasificar automáticamente los commits según el resultado de la prueba, e identificar el primer commit que falló
  • git bisect, que aprovecha el principio de la búsqueda binaria, es una herramienta poderosa para rastrear rápidamente la causa de bugs en bases de código grandes

Algoritmos y un caso real

  • El algoritmo de búsqueda binaria (binary search) va más allá de ser un simple problema de entrevista: también funciona como principio clave en herramientas reales de depuración
  • git bisect puede usarse como una herramienta que utiliza búsqueda binaria para encontrar el “primer commit malo (first bad commit)” que introdujo el bug
    • Funciona con un principio similar al problema “First Bad Version” de Leetcode

El problema en un entorno de trabajo real

  • En un entorno que usa un monorepo grande, pueden producirse cientos o miles de commits al día
  • Es difícil rastrear la causa de una falla de prueba solo con los logs
  • La causa de la falla fue un cambio de cadena en un archivo de configuración necesario para obtener el token de una llamada remota, lo que hizo que se referenciara otra cuenta y provocó la falla de la prueba
  • Ese cambio pasó las pruebas de integración, pero en la práctica causó problemas, y era difícil encontrar en qué punto exacto ocurrió entre tantos commits

Resolver el problema con git bisect

  • Un colega de otro equipo usó el comando git bisect para identificar rápidamente el commit problemático
    • Después de indicar un commit bueno (good) y uno malo (bad), fue haciendo checkout automático de los commits intermedios, ejecutando pruebas y acotando la causa
    • Cada ejecución de prueba tomaba tiempo, pero al final encontró exactamente el commit que introdujo el problema
    • Al revertir ese commit, todas las pruebas volvieron a la normalidad

Cómo se ejecuta git bisect

  • Ejemplo de historial de commits
    • Commit 1: commit inicial (normal)
    • Commit 2: refactorización (normal)
    • Commit 3: se introduce el bug (aparece el error)
    • Commit 4~10: cambios no funcionales (el error continúa)
  • Ejemplo de comandos
    git bisect start  
    git bisect bad HEAD  
    git bisect good HEAD~9  
    git bisect run ./test_script.sh  
    
  • El script de prueba (test_script.sh) devuelve 0 si tiene éxito y un código no normal si falla
  • git bisect hace checkout automático del commit intermedio y ejecuta el script de prueba;
    tomando como referencia el punto en que falla la prueba, identifica el primer commit malo
  • En la salida, se confirma que el commit b982ed9373fe235fe61c74b15faf264bc7142398 es el primer commit con el bug

Conclusión

  • git bisect es una herramienta práctica que aplica el principio de la búsqueda binaria al recorrido del historial de código
  • Incluso en repositorios grandes o historiales de cambios complejos, permite rastrear rápidamente el momento en que se introdujo un bug
  • Combinado con automatización de pruebas, hace posible una depuración estable incluso en bases de código grandes

2 comentarios

 
kandk 2025-11-04

Por este tipo de problemas se usa TBD (trunk-based development).

 
GN⁺ 2025-11-04
Opiniones de Hacker News
  • Cuando antes trabajaba en una base de código enorme, sin cobertura de pruebas y con abstracciones desastrosas, git bisect era casi la única herramienta realmente útil
    El código era tan complejo que seguir un bug de forma lógica era imposible, así que era mucho más fácil encontrar en qué commit apareció el problema
    Pero en bases de código de alta calidad no hacía tanta falta bisect. Se podía probar cada componente de forma independiente y la observabilidad también estaba bien resuelta, así que quedaba claro dónde mirar

    • No creo que git bisect sea algo prescindible. No solo sirve para encontrar bugs, también ayuda a entender por qué ocurrió ese bug
      Si es un proyecto con buenos mensajes de commit, bisect permite captar el contexto de commits pasados y reflejarlo en el commit que corrige el bug. Ese ciclo refuerza la cultura de commits en sí
    • Hace tiempo encontré un bug en un programa OSS donde aparecía una cadena extraña. Era código C y la causa era una variable sin inicializar
      Rastrearlo manualmente era imposible, pero escribí un script de bisect, lo dejé correr unos 30 minutos y encontró exactamente el commit problemático
    • git bisect se introdujo originalmente para encontrar regresiones del kernel de Linux
      Incluso en casos imposibles de probar, como drivers de hardware, hizo posible que usuarios comunes hicieran bisect del kernel por su cuenta para identificar el commit causante
      Antes había que pedir ayuda a desarrolladores por correo electrónico, pero ahora los usuarios podían acotar el problema por sí mismos
    • Si el objetivo es simplemente arreglar el bug, quizá no haga falta bisect. Pero a veces necesitas saber desde cuándo existe el bug
      Por ejemplo, sirve para rastrear el alcance de datos procesados incorrectamente o para decidir si “esto es un bug o una funcionalidad”
    • A veces también necesitas saber cuándo se corrigió un bug
      Por ejemplo, si un cliente sufre el problema en una versión de hace 6 años, puedes comprobar si se resuelve al actualizar a una versión de hace 4 años
      O si el código tuvo un refactor grande, también puedes ver si la corrección fue intencional o accidental
  • git bisect es excelente cuando funciona bien, pero no puede encontrar todos los bugs
    Algunos bugs no muestran síntomas cuando se introducen y solo salen a la luz más tarde por otros cambios
    En esos casos se rompe la premisa de bisect: que el bug aparece una sola vez entre un commit bueno y uno malo
    Los commits que no se pueden probar se pueden marcar con skip, pero si justo ese es el commit problemático, el resultado se vuelve ambiguo

  • Hace poco usé git bisect en serio por primera vez, y fue casi mágico
    Había dos funciones con el mismo nombre, y durante un trabajo de formateo de código se eliminó el import de la función correcta, provocando el problema
    Revisé el código varias veces, pero no entendí la causa hasta que bisect identificó el commit problemático

  • Normalmente ya sé en qué archivo o función apareció el bug, así que no uso bisect tan seguido
    En cambio, sigo el historial de cambios de una función específica con el comando git log -L :func_name:path/to/file.c
    Hace falta configurar .gitattributes

    • Hubo quien preguntó por la configuración de .gitattributes. Quería saber qué hacía falta exactamente
    • También hubo alguien que dijo que usa bisect todos los días. El flujo de trabajo es completamente distinto
    • En casos con funciones polimórficas como en C++, git log -L es débil. Cuesta seguir una versión concreta entre funciones sobrecargadas con el mismo nombre
    • Si no tienes .gitattributes, otra opción es usar git log -S para encontrar commits que contienen una cadena específica
  • Conviene conocer el exit code 125 en scripts de prueba
    Si no puedes determinar el resultado del test, por ejemplo por un fallo de compilación, devolver 125 hace que bisect salte ese commit
    Lo resumí en una entrada de mi blog

    • En repositorios donde los commits de merge marcan puntos que pasaron CI, puede ser útil usar git bisect --first-parent
      Así puedes encontrar rápido “qué PR introdujo el bug”, y luego hacer un bisect más detallado dentro de esa rama
  • Cuando aparece un flaky test, bisect realmente brilla
    Si por una race condition tienes que correr una prueba cientos de miles de veces para estar seguro, dejar un script de bisect corriendo en segundo plano puede volverlo algo solucionable en la práctica

    • En esos casos, parece que aplicar una búsqueda binaria bayesiana podría reducir muchísimo la cantidad de ejecuciones de prueba
  • Hace poco encontré la causa de un bug con bisect en un proyecto de reproductor musical hecho con Svelte (lets-make-sweet-music.com)
    No había pruebas ni logs de error, y como había muchos commits por actualizaciones de dependabot, rastrearlo era difícil
    Gracias a bisect encontré el commit problemático, y la causa fue que el archivo que reemplacé no implementaba la funcionalidad de binding múltiple de eventos
    Si mantienes los commits pequeños, es mucho más rápido acotar la causa del problema encontrado con bisect

  • Alguien dijo que obligar a aprender búsqueda binaria para entrevistas era forzado, pero git bisect es un gran ejemplo real de ese concepto
    Aunque no hace falta implementarla a mano. La mayoría de los lenguajes ya la ofrecen en su biblioteca estándar

    • Curiosamente, se dice que la búsqueda binaria se propuso por primera vez en los años 40, pero una implementación sin bugs no apareció hasta los años 60
      Si calculas el índice medio con (low + high) / 2, puede producirse overflow
    • Personalmente, creo que todo desarrollador debería implementar búsqueda binaria al menos una vez en un lenguaje con enteros de precisión arbitraria (por ejemplo, Python)
      Es uno de los mejores ejercicios para entrenar el pensamiento basado en invariantes
  • Además de bisect, Git tiene excelentes herramientas para explorar código, como log -L, log -S y blame
    Hace tiempo escribí una entrada de blog sobre este tema