wandres.dev
SOURCES I · Depurar con breakpoints

El paso a paso: cuatro modos y cuándo usar cada uno

Continuar, pasar por encima, entrar, salir y el paso genérico, con la estrategia de navegación que evita perderse en el código de las librerías.

⏱ 15 min

Una vez detenido, el depurador ofrece cinco formas de avanzar y la diferencia entre elegir bien y elegir mal es acabar en tu siguiente línea o acabar treinta marcos dentro del planificador de un framework sin saber cómo has llegado ahí. Los nombres de los controles no ayudan mucho a decidir; lo que ayuda es tener claro qué pregunta responde cada uno.

🎯 Al terminar esta lección sabrás
  • Elegir el modo de avance según la pregunta que quieras responder.
  • Distinguir el paso por encima del paso genérico y saber cuándo importa la diferencia.
  • Continuar hasta una línea concreta sin poner un breakpoint temporal.
  • Navegar código asíncrono con el paso a paso sin perder el hilo.

Los cinco controles

ContinuarF8, o Cmd+\\ y Ctrl+\\— reanuda la ejecución hasta el siguiente breakpoint o hasta que el programa acabe. Es el que más se usa y el que menos explicación necesita.

Pasar por encimaF10— ejecuta la línea actual completa, incluidas todas las llamadas que contenga, y se detiene en la siguiente línea del mismo marco. Responde a la pregunta “¿qué hace esta función en su conjunto?”.

EntrarF11— ejecuta hasta la primera llamada de la línea actual y se detiene dentro de esa función. Responde a “¿qué pasa ahí dentro?”.

SalirShift+F11— ejecuta lo que quede de la función actual y se detiene justo después de la llamada que la trajo aquí. Responde a “ya he visto suficiente, sácame de aquí”.

PasoF9— es el modo genérico: avanza a la siguiente sentencia que se vaya a ejecutar, entrando o no según toque, sin la lógica de saltar librerías ignoradas.

La diferencia entre el paso genérico y el paso por encima es sutil y a veces decisiva: el genérico entra en código que el paso por encima saltaría, incluido el código de la lista de ignorados. Cuando sospechas que la respuesta está justo en la librería que estás saltando, es el modo que la alcanza sin desactivar la configuración.

💡
Tip

Los atajos de función se pueden cambiar por el preset del estilo de Visual Studio Code en los ajustes de las DevTools. Si tu editor es ese, unificar las teclas del paso a paso entre editor y navegador elimina un cambio de contexto mental que se paga muchas veces al día.

Continuar hasta aquí

La operación más útil del paso a paso y la que menos gente conoce. Con la ejecución detenida, el botón derecho sobre cualquier número de línea del fichero actual ofrece continuar hasta ahí. Equivale a poner un breakpoint temporal en esa línea, continuar, y quitarlo.

Sustituye a las secuencias de veinte pulsaciones de paso por encima que son la forma habitual de recorrer una función larga. Si sabes que lo interesante está en la línea 340 y estás en la 300, un clic derecho te lleva allí con todo el estado intermedio ya ejecutado.

Y tiene una variante muy potente: funciona hacia atrás en el sentido de las iteraciones. Si estás dentro de un bucle y pides continuar hasta una línea anterior del mismo bucle, la ejecución avanza hasta la siguiente vuelta.

La estrategia de navegación

Recorrer código con el depurador sin estrategia produce la sensación de estar perdido, y esa sensación es la razón principal por la que mucha gente lo abandona. La estrategia que la evita tiene tres reglas.

Regla uno: pasa por encima por defecto, entra solo cuando tengas una razón. El instinto inicial es entrar en todo para no perderse nada, y el resultado es acabar dentro del código de un framework en el tercer paso. Al revés funciona mucho mejor: recorre tu función completa pasando por encima, observando cómo cambian las variables en el panel de scope, y cuando una llamada produzca un valor que no esperabas, vuelve a ejecutar y entra solo en esa.

Regla dos: si has entrado donde no querías, sal inmediatamente. No intentes recorrer el código de la librería hasta encontrar la salida. El botón de salir te devuelve en un paso.

Regla tres: configura la lista de ignorados antes de empezar. Con las librerías ignoradas, el paso por encima y el entrar dejan de meterse en su código y el recorrido se vuelve el de tu aplicación. Esto es tan determinante que tiene su propia lección más adelante; sin ello, depurar una aplicación con framework moderno es una experiencia bastante desagradable.

El paso a paso en código asíncrono

Aquí es donde el modelo mental hay que ajustar. Cuando pasas por encima de una línea con await, la función se suspende y el control vuelve al bucle de eventos. El depurador tiene que decidir qué hacer.

El comportamiento es detenerse cuando la ejecución reanuda en esa misma función, lo cual es casi siempre lo que quieres: pides el paso, la promesa resuelve, y te encuentras en la línea siguiente con el valor ya disponible. Entre medias han podido ejecutarse otras cosas.

Dos consecuencias que hay que tener presentes.

El tiempo real pasa mientras estás parado. Si te detienes tres minutos en un breakpoint dentro de una función asíncrona, las peticiones en vuelo completan, los temporizadores acumulan retraso, y las conexiones pueden expirar. El orden de eventos que observes puede no ocurrir nunca en condiciones normales.

Otras tareas se ejecutan entre tus pasos. Si tienes un breakpoint en otro sitio y pasas por encima de un await, es perfectamente posible que la ejecución se detenga en ese otro breakpoint, en otra función, antes de volver a la tuya. No es un fallo: es que el bucle de eventos hizo otra cosa mientras tanto.

Para seguir un flujo asíncrono largo, el paso a paso es la herramienta equivocada. Lo correcto es poner breakpoints en los puntos clave y usar continuar, apoyándose en la pila asíncrona para reconstruir el recorrido.

La regla de las tres líneas antes de dar un paso

Existe un antipatrón del depurador que es exactamente el equivalente del “cambiar y rezar” de las trazas, y que produce sesiones larguísimas sin resultado: avanzar mecánicamente, un paso tras otro, esperando que algo llame la atención. Con esa forma de trabajar, un breakpoint bien puesto se desperdicia, porque el depurador se convierte en un reproductor de vídeo del que solo miras si algo parece raro, y lo raro casi nunca es visualmente raro. La disciplina que lo cambia todo cuesta cinco segundos por paso y es contraintuitiva: antes de pulsar el botón de avanzar, predice en voz alta o mentalmente qué van a valer las tres variables que te importan después de esa línea. Luego pulsa y comprueba. Las dos únicas cosas que pueden pasar son valiosas. Si aciertas, has confirmado tu modelo mental de esa parte del código y puedes avanzar rápido y con confianza por toda la zona. Si fallas, acabas de encontrar el punto exacto donde tu comprensión del programa se separa de lo que el programa hace, y ese punto es la definición literal de dónde está el bug —o dónde está el malentendido que te impide verlo, que a efectos prácticos es lo mismo. La diferencia con avanzar mirando es enorme, porque mirando puedes pasar quince veces por encima del valor incorrecto sin registrarlo: sin una predicción previa, no hay nada con lo que contrastar y el ojo solo detecta lo espectacularmente anómalo. Un undefined donde esperabas un array llama la atención; un 0 donde esperabas un 1, o un array de tres donde esperabas cuatro, no la llaman en absoluto, y son exactamente el tipo de discrepancia donde viven los bugs difíciles. La regla, resumida en una frase que merece la pena repetirse al empezar cada sesión: el depurador no sirve para ver qué pasa; sirve para comprobar si pasa lo que crees.