New Debugging Paradigms for Object-Based Software Development
The object methodology is one of the first major shifts in software development in the last 25 years. The creation of objects, where an object represents a functionally intelligent grouping of state and operations whose details can be concealed, gives the software developer a higher metalevel upon which to base a solution creation. Object methodology, as currently practiced, is the culmination of the information-hiding methodology proposed by Parnas and refined through decades of practical use.
In the last couple of decades, the paradigm of Object Methodology has hit the mainstream computing market; it is no longer an arcane art practiced by academics or megaline-system developers, but instead is readily available to even all programmers. Object implementations such as the Microsoft .NET Framework has made the technology pervasive.
The availability of object methodology has allowed us to design and construct software systems far more complex than we could have imagined even a decade ago. One of the problems that have arisen, however, is the very abstraction we use to create these complex systems can not only mask their complexity during development, but can mask their complexity during debugging and post-deployment maintenance. The original programmers can often translate their mental models of objects into code, but the maintainers–in fact, even the programmers themselves mere months later–must "reverse engineer" these structures back into a mental model.
Debugging Versus Browsing
Debugging, in contrast to the sophistication of our construction techniques, has remained largely unchanged over the same period of time. We have very few debugging tools that are object-modeled, at least in the mainstream computing world. Instead, we still set breakpoints, use debug-print statements, and display the contents of memory locations in an attempt to figure out what has gone wrong.
It is important here that we want to contrast debuggers with browsers. A browser allows us to inspect code, study module relationships, display call graphs, and generally inspect the static representation of a program. There have been some quite nice browsing environments developed, both independent of, and as integrated components of, compilation systems. However, by our definition, a debugger is a tool that allows us to inspect, in some way, the dynamic behavior of a program as it is executing.
Debugging is often an exercise in "reverse engineering." We must deduce, from the current program state and current execution point, how the current state and execution point were reached, and why, and what can be done to correct them if the state or execution point (or both) are incorrect.
Reverse engineering is also known as design recovery. However, the people practicing "design recovery" are often working at a much higher structural level than is useful for debugging. Or, to hearken back to an issue of the 1970s, design recovery is working at the "design-in-the-large" level and debugging is working at the "design-in-the-small" level.
The problem in both cases, however, is quite similar: reconstruct a model of the program that can be understood by the reader. In many cases we find the design-recovery-in-the-large uses visual notation — for example, E-R diagrams, Dean-Cordy diagrams [DC95], UML, and so on. For decades (and, in some more regressive contexts, even today) flowcharts are used to describe code at the statement level. These representations have much in common in terms of human perception; they involve a sensory channel capable of handling vast amounts of information: the visual system.
The cost of computers, and the cost of specialized peripherals, for decades limited us in how we could interact with the computer. We debugged by lights-and-switches, core dumps, octal or hexadecimal displays of individual words or bytes, and so on. The limited bandwidth with which we could interact with the computer was a significant barrier in the understanding of the software systems we were trying to debug.
Today, the balance has changed. High-resolution full-color bitmap displays mean that we can often animate our algorithms in a graphical fashion. The cost of interaction is negligible compared to the cost of development overall. Computers are, for practical purposes, nearly free compared to the people who use them. Thus we can get graphical displays of our data structures, chase down complex pointer chains with a series of mouse clicks, see our data in an almost-readable form, and even watch algorithms in action.
But what’s missing from this picture? It turns out that our new tools still only show us pieces of the algorithm, often in terms of the computer’s representation, not our mental model. And while we are utilizing the visual channel to a higher degree, we have virtually neglected another significant human input channel, the auditory system. Thus, we want to approach the problem of debugging from two viewpoints: a better presentation of visual information, and use of the human auditory channel as a means of enhancing our ability to both build our cognitive model and interpret the program’s behavior in terms of that cognitive model.