Tools12 min read

Using Breakpoints Effectively: Beyond Step and Continue

Most engineers know F9 to set a breakpoint and F5 to run. But breakpoints have hidden superpowers — conditional, logpoint, hit-count, and data breakpoints — that can cut debugging time in half. Here's how to use them with real examples.

debuggingbreakpointsvscodechrome-devtoolsnodejs

I've seen too many engineers sit through five minutes of pressing F10, F11, Shift+F11, over and over, trying to spot where a variable goes wrong. They could have finished debugging in 30 seconds with a conditional breakpoint. Breakpoints are not just pause buttons — they're programmable probes.

In this post I'll cover breakpoint types that most people skip: conditional, logpoint, hit count, data breakpoint, and function breakpoint. I'll show real examples from VSCode and Chrome DevTools, plus a war story where a data breakpoint saved a production outage.

The Two Kinds of Standard Breakpoints

There's the line breakpoint (F9 in VSCode, click line number in DevTools) — that's the basic one. Then there's the function breakpoint: pause when any code enters a specific function, regardless of file or line. In Chrome DevTools, go to the Sources tab, click the + icon in the Breakpoints section, and type a function name (e.g., `fetch`). This is handy for intercepting API calls without modifying source.

I use function breakpoints when I need to know where a particular function is called from — especially when dealing with third-party libraries or minified code.

Conditional Breakpoints: Stop Only When It Matters

Your typical loop debug looks like this: set breakpoint inside loop, press F5 a hundred times, lose count, get annoyed. A conditional breakpoint stops only when a condition is true. In VSCode, right-click the breakpoint dot, select 'Edit Breakpoint', and enter an expression like `i >= 50 && user.timeout > 2000`.

I once debugged a WebSocket reconnection bug that only happened on the 37th reconnect. A conditional breakpoint on `reconnectCount === 37` caught it instantly.

Place a conditional breakpoint on the console.log line with condition `i === 37`. It will pause only on that iteration.
// Example: conditional breakpoint in VSCode
for (let i = 0; i < 1000; i++) {
  const result = processItem(items[i]);
  // Set conditional breakpoint on this line with: i === 37
  console.log(`Iteration ${i}: ${result}`);
}

A well-placed conditional breakpoint is worth a thousand step-overs.

Logpoints: Log Without Stopping

Sometimes you don't want to pause — especially in a hot loop or when the bug involves timing. Logpoints (called tracepoints in some IDEs) let you log a message to the console without breaking execution. In VSCode, right-click the gutter, select 'Add Logpoint', and type something like `'user id:', user.id, 'timeout:', timeout`.

I use logpoints when I need to trace a series of events without altering source code or dealing with console.log cleanup. They're also great for production debugging if you can attach a debugger (e.g., Node.js inspect).

In VSCode, the logpoint output appears in the Debug Console automatically.
// Logpoint example — log without pausing
function handleRequest(req) {
  // Add logpoint: 'req.method:', req.method, 'url:', req.url
  processRequest(req);
  // Add logpoint: 'after process, status:', res.statusCode
}

Hit Count Breakpoints: Stop After N Iterations

Hit count is a specific kind of conditional breakpoint that stops after the breakpoint has been hit a certain number of times. In Chrome DevTools, right-click a breakpoint and choose 'Edit breakpoint…', then enter a hit count condition like `>= 10`.

This is different from a conditional breakpoint because you don't need to evaluate an arbitrary expression — you just say "stop on the 10th hit". It's faster and simpler for cases where the bug appears after a fixed number of passes.

The Production Incident That a Data Breakpoint Solved

  1. 09:47Users report that order statuses randomly change from 'shipped' to 'pending'.
  2. 10:12Engineers suspect race condition but can't reproduce locally.
  3. 11:30After adding logpoints, they see the `status` field mutates in an unexpected place.
  4. 11:45Set a data breakpoint on `order.status` in the debugger — it triggers on a forgotten `Object.assign` in a callback.
  5. 12:00Fix: replace `Object.assign` with immutable update. Bug gone.

Lesson

Data breakpoints (watchpoints) pause when a variable's value changes, not when code hits a line. They are invaluable for tracking down who is mutating data. In Chrome DevTools, right-click a variable in the Scope panel and select 'Break on…' → 'Property changes'.

Data Breakpoints (Watchpoints) in Practice

Data breakpoints are different from line breakpoints: they watch a memory location or object property and pause whenever it changes. In Chrome DevTools, you can set a breakpoint on a DOM node's modification (subtree, attribute, removal) — that's a data breakpoint for the DOM. In Node.js, you can use the `--inspect` flag and the DevTools interface to set watchpoints on objects.

One caveat: data breakpoints can be slow if the watched variable changes frequently. Use them sparingly, and combine with conditional logic if the debugger supports it.

Data breakpoints are especially useful for debugging unexpected state mutations in React or Redux.
// To set a data breakpoint in Chrome DevTools:
// 1. Add a breakpoint in your script (any line).
// 2. When paused, go to the Scope panel.
// 3. Right-click a property (e.g., 'this.status') and select 'Break on...' -> 'Property changes'.
// The debugger will pause whenever that property is set to a new value.

Exception Breakpoints: Catch Errors at the Source

Most debuggers let you break when an exception is thrown, even if it's caught. In VSCode, there's a checkbox for 'Uncaught Exceptions' and 'Caught Exceptions'. In Chrome DevTools, you can pause on caught exceptions as well.

I use caught exception breakpoints when I have a try/catch that swallows an error silently. It's saved me hours of adding console.log inside catch blocks.

  • arrow_rightSet 'Uncaught Exceptions' breakpoint: pauses only when an exception is not caught — good for preventing crashes.
  • arrow_rightSet 'Caught Exceptions' breakpoint: pauses on every exception, even if caught — use with caution; can be noisy.
  • arrow_rightIn Node.js, you can also break on specific error types by filtering in the debugger UI.

Putting It All Together: A Real Debugging Session

Suppose you have a React app where a user's profile picture sometimes doesn't update after they upload a new one. The bug is intermittent. Here's the approach:

1. Set a logpoint in the upload handler to log the new URL. This confirms the upload works and the URL is correct.

2. Set a function breakpoint on the component's `render` function to see if it's called after the upload.

3. Set a data breakpoint on `this.state.profilePicture` — pause when it changes. You might find that a parent component resets the state after the upload callback.

4. Use a conditional breakpoint inside the parent's `componentDidUpdate` to break only when `prevState.profilePicture !== this.state.profilePicture` and the new value is null.

In 10 minutes, you'd have the root cause: the parent's reducer was overwriting state on a different action that happened to fire after the upload.

10x

Faster debugging with conditional breakpoints vs. manual stepping

Tips for Effective Breakpoint Usage

  1. 1Name your breakpoints: In VSCode, you can add a label to each breakpoint in the Breakpoints pane. This helps when you have many.
  2. 2Disable breakpoints without removing them: In both VSCode and Chrome, you can toggle breakpoints on/off individually — useful for debugging multiple issues.
  3. 3Use breakpoint groups: In Chrome DevTools, you can create breakpoint groups (e.g., 'auth', 'api') by using the `// #breakpoint-group` comment.
  4. 4Combine with watch expressions: While paused, add watch expressions for complex conditions — e.g., `myVar.length > 10 && myVar.startsWith('err')`.
lightbulb

In VSCode, you can set a conditional breakpoint that logs to the console using a logpoint — but you can also set a conditional breakpoint that calls a function (with caution, as it modifies execution). Use `console.warn` in a conditional expression to get stack traces without pausing.

When Breakpoints Don't Work (and What to Do)

Breakpoints fail when: code is optimized (production builds), code is in a different thread (Web Workers, child processes), or when the debugger can't attach (e.g., remote debugging without proper setup).

For production debugging, consider using `debugger` statements with a conditional guard (e.g., `if (process.env.DEBUG) debugger;`) or use Node's `--inspect` with breakpoints in the DevTools. For Web Workers, you need to open a separate DevTools instance for the worker.

When all else fails, revert to strategic console.log with a timestamp and a unique identifier — but by then you should already have the data breakpoint that saves the day.

Frequently asked questions

What is the difference between a conditional breakpoint and a logpoint?

A conditional breakpoint pauses execution only when a condition (e.g., `i === 5`) is true. A logpoint (or tracepoint) logs a message to the console without pausing — useful for tracing values in hot loops or production-like scenarios.

Can I set a breakpoint that triggers only after a certain number of hits?

Yes. In VSCode and Chrome DevTools, you can set a hit count condition (e.g., break on the 10th hit). This is useful when a bug occurs after many iterations of a loop.

How do I set a breakpoint when a variable changes?

Use a data breakpoint (also called a watchpoint). In Chrome DevTools, right-click a variable in the Scope panel and select 'Break on...' → 'Property changes' or 'DOM changes'. In native debuggers like GDB, use `watch myVar`.

Are breakpoints slow? Should I be worried about performance?

Standard breakpoints freeze the thread, so they are not slow for typical use. Conditional breakpoints involve expression evaluation each time the line is hit, which can be slow in hot loops. Logpoints are also evaluated but don't pause, so they have minimal overhead in most cases.