Tuesday, November 17, 2009

XDepend (now JArchitect): managing code complexity

At a NEJUG presentation I attended a while back, the presenter used a vivid metaphor of taming the "complexity dragon", meaning the tendency of a code base to become brittle and complex as it grows in size. This dragon can cripple even competent development teams: productivity plunges because each code change triggers bugs in other obscure parts of the program. That said, the presenter hastened to clarify that he meant European dragons, since apparently dragons aren't necessarily evil creatures in other cultures.

A major contributor to large code bases becoming brittle is the dependencies between pieces of code. If class A uses class B, then A depends on B and a change in B may cause A to malfunction. That's not too bad if there are only 2 classes, but becomes impossible to track manually as code bulks up. The difficulty is that complexity, as measured by the number of dependencies between units of code, does not merely increase linearly with the quantity of code. In fact, the function is quadratic and the curve is parabolic.

This is why architecture-minded folks try to impose certain code architectures: to tame -- though never quite slay -- the dreaded complexity dragon. When architectural layers are enforced, code in certain layers cannot directly use -- and depend on -- code in certain layers, and generally dependencies only go in one direction. These boundaries break up the n^2 function. Or they may try to eliminate cyclic dependencies: cycles couple code units together, preventing them from being tested or used in isolation. Or they may use various code metrics to discover potential trouble spots.

This may sound admirable, but for any code base big enough for such architectural enforcement to be worthwhile, I can't imagine doing this exploration by hand. I'd need a tool. Fortunately, there seems to be quite a number of tools out there. The folks responsible for XDepend (now called JArchitect) were kind enough to give me a copy to review. I don't pretend to be any kind of authority on this topic, nor even come close to mastering this tool. But I think this tool is cool enough for me to share a few of its features.

Dependency matrix



Sunday, November 15, 2009

On conciseness

I meant to write this thought for a while, but a recent blog post about implementing the Quicksort algorithm in Scala finally prompted me to write this. As a refresher, here is pseudocode from the Wikipedia entry on quicksort that illustrates a simple version of the algorithm.

function sort(array) // pseudocode from Wikipedia
    var list less, greater
    if length(array) ≤ 1 return array  
    select a pivot value pivot from array
    for each x in array
        if x < pivot then append x to less
        if x > pivot then append x to greater
    return concatenate(sort(less), pivot, sort(greater))

From the Groovy in Action book, here is a Groovy implementation. It works on a List of objects that implement the compareTo() method. The findAll method returns elements from the list that satisfy the given expression, with "it" representing the object in the list.

def sort(list) { 
    if (!list) return list
    def pivot = list[0]
    def less    = list.findAll {it < pivot}
    def same    = list.findAll {it == pivot}
    def greater = list.findAll {it > pivot}
    sort(less) + same + sort(greater)
}

What I find significant about this example is that the real-code, working implementation of an algorithm is every bit as concise and readable as the pseudocode used to explain the algorithm.

From the same book, here is a Java implementation:

    public static void qsort(Comparable[] c,int start,int end){
        if(end <= start) return;
        Comparable comp = c[start];
        int i = start,j = end + 1;
        for(;;){
            do i++; while(i<end && c[i].compareTo(comp)<0);
            do j--; while(j>start && c[j].compareTo(comp)>0);
            if(j <= i)   break;
            Comparable tmp = c[i];
            c[i] = c[j];
            c[j] = tmp;
        }
        c[start] = c[j];
        c[j] = comp;
        qsort(c,start,j-1);
        qsort(c,j+1,end);
    }

    public static void qsort(Comparable[] c){
        qsort(c,0,c.length-1);
    }


This contrast is not completely fair to Java, since this Java version does an in-place sort of the array. Nevertheless, I think it illustrates the look of typical Java code.

Clearly, the newer JVM languages like Groovy allow for concise code. But I suggest that the real value of such conciseness comes not from reduced typing, but from the improved readability of the resulting code. You will read a piece of source code far more often than you will write it. It helps enormously -- whether you are enhancing or debugging code -- if you can comprehend meaning of the code immediately. In this example, the Groovy code's meaning comes through as clearly as the pseudocode. By contrast, the Java code is cluttered with the tedious mechanics of achieving the desired result. Here, the Groovy code emphasizes the what -- the meaning -- while the Java code emphasizes the how -- the process.

Of course, if performance is the primary consideration then it makes sense to write less concise code. See, for example, the various implementations of Quicksort in java.util.Arrays. And I am glad that we have performance-tuned libraries. But most application code has little impact on overall application performance, and for such code I suggest that the readability and conciseness that comes from the expressiveness of languages like Groovy is a big win.