Better Debugging with Conditional Breakpoints

By  on  

I love conditional breakpoints. Really! They're my favorite debugging tool.

When I got started in web development, "debugging" for me meant creating a <pre id='log'></pre> and appending strings to its contents to act as a log. But once Firebug rolled around—and then when browsers started baking in their own dev tools—it was like upgrading from a skateboard to a private jet. Breakpoints, watches, call stacks, profilers, network activity monitors—they're all useful, and I wouldn't want to lose any of them.

But conditional breakpoints are my favorite, and it's not even close. Here's how I use them:

Breaking Only in Certain Conditions

The obvious case is the one that's documented everywhere: creating a breakpoint that only pauses execution when a particular expression evaluates to true.

Screenshot showing an example of setting a conditional breakpoint.

Using conditional breakpoints this way is nice when I'm trying to track down some weird behavior in a section of code that runs often, but whose behavior is broken only in the presence of specific combinations of data. A normal breakpoint would just pause execution every time and debugging would be tedious, but a conditional breakpoint allows you to pause only when the right data are present, so you can stop and look around. Nice.

But that's the mundane usage. Honestly, it's probably the least common way I use them. You see, conditional breakpoints are a scalpel. They're a monkey patcher's dream.

Exporting Variables to the Global Scope

Have you ever been in a situation where you wanted console access to a variable defined locally in a function, but from an execution context outside the function? This happens to me all the time; I want to let my app load and run until an idle state, and then be able to inspect, say, properties or methods on some object locked away in a closure. Conditional breakpoints to the rescue!

Screenshot showing a conditional breakpoint that assigns a local variable to window._STATE

The main trick here is to use the lowly comma operator to make sure that the assignment doesn't evaluate as truthy, because that would cause the breakpoint to pause execution. Instead, the breakpoint expression evaluates to false and the app flies right through it and runs until idle, and then you can inspect the value in the console to your heart's content just by typing its name.

Note: I make a habit of doing window.varName rather than just varName so I don't accidentally modify a variable that exists in an outer scope relative to the location of the breakpoint.

Protip: in an ES2015+ enabled browser, export a series of variables quickly with shorthand property names: window.dealyBob = {var1, var2, otherVar}, false

Using the comma operator this way is the key to making conditional breakpoints sing.

Adding Logging Without Editing Your Code

My most common use case for conditional breakpoints is logging. I know it's common among professional developers to poke fun at console.log-driven development, but being able to instrument your code without rebuilding or even reloading, watch everything run in real time, and get detailed diagnostic output is fantastic.

Screenshot showing a conditional breakpoint that does console.log() with some data held locally inside a function

What's wonderful about this is, the Dev Tools will save the breakpoints' associations with the file(s) in question (at least in Chrome, where I tend to work most often these days), so they're still there the next time I load the app in a different session, without me actually having to save any changes to my app code! This gives me a kind of runtime Aspect-oriented logging system that lives purely in the browser. How's that for separation of concerns?

Modifying Data

Say you have a bug where the repro is to have a particular combination of data loaded, and to get to that state you have a number of tedious steps to follow first. Not anymore! As a keen reader, I'm sure you noticed earlier that if you can modify properties on window to create new global variables in a conditional breakpoint expression, there's nothing stopping you from modifying anything else.

Screenshot showing some code assigning values to properties on an object held locally inside a function

So go ahead and paste a bunch of JSON into a conditional breakpoint and assign it to whatever variables you need. Boom! Say goodbye to the tedious repro.

Protip: the comma operator allows you to chain more than just two statements together, so if you have a whole set of assignments to make, go right ahead and say: (var1 = x; var2 = y; var3 = z), console.log('overriding with', x, y, z), false

Related Protip: don't forget that you can set values on any global object from the console; if you have particularly large objects to use as overrides, or if you want to change the data a conditional breakpoint will use without having to modify the actual breakpoint, get thee to the console and say window.bigOverrideObject = {pasteYourObjectHere}, and then in the conditional breakpoint expression, var1 = window.bigOverrideObject, false

Injecting and Testing New Code

Insightful reader that you are, you've likely realized that conditional breakpoint expressions are just JavaScript code that runs in the scope & context in which they're placed. If you can make assignments or write to the console in a conditional breakpoint, why not use one to test new application code? Yep.

Screenshot showing a conditional breakpoint that computes a document word count and displays it in the UI

Insert a conditional breakpoint anywhere you like, and run whatever you want! There are a few limitations—for example, you can't return from the current function directly in the breakpoint expression—but for the most part, you can do whatever transformations or computations your app needs.

This is where the monkey patching aspect comes in: you can combine all of these techniques and use conditional breakpoints to overwrite entire functions, even when they're inside a closure. Check it:

Screenshot showing a conditional breakpoint that overrides an entire inner function

Pretty sneaky, sis! (warning: '80s kid reference)

Protip: your dev tools obviously aren't modifying the deployed app code, so this makes for a great way to try things in your production system without doing a whole build/deploy cycle. Be careful not to tweak things in such a way that they end up wrecking your production data, however!

Conclusion

I love conditional breakpoints. And now I hope you do too!

PS: special thanks to my pal and fellow conditional breakpoint enthusiast Brian Sinclair for reviewing this article, and for the conversation that inspired it. His love for conditional breakpoints is truly unconditional.

Revin Guillen

About Revin Guillen

Revin is a long-time web developer who's been writing JavaScript apps since the mid '90s, working in small companies, Large Enterprises™, as an employee, and on a consulting basis. He's seen a lot of things.

Recent Features

  • By
    Responsive Images: The Ultimate Guide

    Chances are that any Web designers using our Ghostlab browser testing app, which allows seamless testing across all devices simultaneously, will have worked with responsive design in some shape or form. And as today's websites and devices become ever more varied, a plethora of responsive images...

  • By
    fetch API

    One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to...

Incredible Demos

Discussion

  1. firas

    I haven’t got it :how to make those conditional breakpoints?

  2. Revin Guillen

    @firas oh you’re right I skipped that, didn’t I? You can right click the line number in the code and there’s typically an option like “Add Conditional Breakpoint…”, or you can click the line number to create a normal breakpoint, then right click it and do “Edit Breakpoint…”

  3. csaldan

    Thanks for the article. Is there a way to export the breakpoints? so that I can share it with other team members or do they have to create them manually?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!