We noticed that as users were trying to inspect the values in their code, they spent most of their time interacting with the debugger UI, as apposed to thinking about their debugging problems. Some of the work that they had to do for the debugger included:
Since we observed over and over again that users chose to use datatips (the tooltips that you get when you hover over a variable) as their tool of choice for inspecting values, since datatips don’t require much “management” in terms of sizing, placing, etc… The debugger team, wisely in my opinion, invested heavily in enhancing how datatips worked. (For those of you who haven't seen this feature yet, data tips now let you drill down into a value so you can see what's inside a variable without going to a debugger window.)
An interesting aside here, our user research suggests that even hard core Unix developers who never use the mouse when writing code, do use the mouse when debugging code. Once of them told us that debugging is a visual activity, and as such, the mouse is efficient and natural while doing that task.
As cool and efficient as datatips are, I think we need to take it even farther. Why should users have even have to hover with the mouse to see the values? I think we should move toward a model where the watch window is basically embedded in the editor. Maybe with some gesture like clicking the mouse button
Often, when a user is debugging a framework object, for instance a Dataset, the actual data that the user wanted to find was essentially inaccessible because the debugger simply showed the internal data structure. So if you wanted to see what data was in a Dataset that you filled, you had to dig way down into the watch window, and hope you could find the information you needed.
The debugger team’s solution to this problem was Visualizers. I think Visualizers will save developers countless hours of debugging. (Again, if you haven't seen these yet, Visualizers provide a way to display information about an object on a Win Form, so developers can focus on the interesting part of the data. For example, if you are debugging a program that uses a dataset, you can view the data in the dataset in a grid, since this is usually what you want to know when debugging. Visualizers are an additional debugging method, you still have the ability to look at the dataset in a debugger window as before.)
We saw a lot of users trying to inspect values that didn’t fit into the value column of a debugger window very well. For instance when a user wants to see the structure of some XML that they are reading in they have to write it to the output window to really see the structure of it and to be able to scroll through it effectively. Unfortunately this requires writing extra lines of debugging code. Then there are other data types, like bitmaps, which you simply can’t view in the debugger at all.
Visualizers to the rescue again! Now you can view strings, XML, etc… from the debugger in a reasonably readable format.
Users frequently want to know the return value of a function call they are making. Most users end up rewriting their code to set the return value to a local variable so they can see that value in a debugger window. This can make code hard to read and unnecessarily complex. Plus, it’s just extra work that users have to do for the debugger.
It turns out that some users discovered that they can add a method call to the watch window (not many C# users seem to use the immediate window to try out function calls). Unfortunately, for the users who did add the values to the watch window, they sometimes got foiled by side effects of methods. For example, we saw one user trying to debug a program that was reading in XML. He tried to see what the he was reading in by putting a “read()” method in the watch window. In this case, the read method read in the next node of XML, but also moved the cursor in the XML file to the node after. As a result, the debugger stole half of the XML and made the program act oddly.
This is a tough problem, and probably users will still have to rewrite code at least some of the time to debug effectively. However, there are a couple of features that the debugger team worked on in this version that should help address this problem. One thing they tried to do was to make it clear that in the watch window the method was executed, it didn’t just store the last return value.
We saw some users writing code for big projects, so they had a lot of code. Sometimes they would see a bug, but it was very hard for them to figure out where to set the first breakpoint to start stepping and inspecting, especially when the bug only shows up after some moderate user interaction with their program. While there are a couple of new features that will assist with this is a tough problem because a key user task that is not well supported by the debugger is...
Often developers have to figure out how a program works in a general way to start to get an idea about what could be causing a bug, or even where to set a breakpoint and start looking at it more closely. The thing that the debugger does so well is to provide a very detailed snapshot of the program, but it doesn’t do as good a job helping a developer understand how the program executes, it doesn’t give an overview. It’s like trying to understand the plot of a movie by looking at a few frames from the film.
From the point of view of the psychology of programming, I think this is a very interesting area to understand and to try to support developers. I know there are various profiling tools, etc… (also, see the Trace Points feature in Whidbey) that can help developers look at stack traces and event logs, etc… but I haven’t seen any tools that seem to be built on a really solid foundation of how developers think about this. I think designing a debugger that helps a developer with this cognitive task would be an incredible benefit.
When developers build a mental model of how a program works, what do those models look like? How do they use tools to help build those models? How do they use those models to enhance or debug a program?