(Matt
Fowles is a Senior Software Engineer for StreamBase, whose interests
include programming language, compiler, and virtual machine design.)
As the ponytail (and the tiny bio above) imply, my posts will focus on technical details such as language semantics, general programming issues, compiler design, or anything else I feel is nifty.
Bob Hagmann from Aleri recently posted a great piece on designing and debugging CEP apps; unfortunately, he leaves out what that process of debugging might actually look like. While some languages force you to spelunk through printf output or trace files to track down a bug, I have found that the returns from an integrated debugger are immeasurable (and greatly magnified by good design). StreamBase ships with a visual debugger built into its development environment, so I will expand on Bob's post with a concrete example using the StreamBase visual debugger. Consider the following sample application that continuously calculates Bollinger Bands for stock quotes (for those playing along at home this sample ships with StreamBase and can be loaded with "File -> Load StreamBase Sample...")
This app actually calculates Bollinger bands in two different ways, one using a simple moving average and the other using an exponential moving average. For simplicity, the two paths are separate branches of the main app, allowing each path to be debugged separately. Within each path, the calculation is divided into a first step, where the moving average and standard deviation are calculated, and a second step, where the intermediate results are used to calculate the Bollinger bands. This allows us to use the StreamBase debugger to inspect intermediate results and inspect their values.
With the app paused at a breakpoint, we can inspect the fields of the current tuple or at any earlier point in the active data flow, allowing easy inspection of transformations and invariants that need to be maintained. The StreamBase debugger also lets the user modify the value of fields in the current frame before resuming execution, providing a simple way to test downstream components with hand crafted data.
Of course in an application this simple, one doesn't even need a debugger or careful design. But, when debugging a larger application like a smart order routing system, organizing your code into staged transformations where each stage is a submodule can be invaluable.
This
sort of design not only allows you to step into each module and debug
its inner workings separately, but it also provides clean abstraction
barriers for unit testing. And once you have unit tests for each stage
and a debugger to help you fix regressions, you will be free to
refactor, optimize, and extend your application with impunity.