The structure of Struts is characterized by a nasty dependency tangle that starts at the class-level and percolates up through the package and design-levels.
Following my blog on Spring’s “almost perfect” structure, I thought I’d take a look at another open source project in a similar space to perhaps shed a light on common structural problems through comparison. Struts sprang to mind, it being already published to my structure101 repository. This is a quick overview of the structure of Struts.
Struts comprises about 97KLOCs (estimated from byte-code) in 82 packages, compared to Spring’s 70KLOCS in 139 packages.
Spring’s achievement was to grow a sizable code-base while avoiding package-level tangles. Struts did not manage this:
The key point in this case is that the leaf packages (the ones that directly contain classes) are 12% tangled, spread over 10 separate tangles. This is not the worst I’ve seen by any means (more another day, perhaps), but it should be a cause for concern for a code-base of this size that is presumably under continuing development. The classes are a respectable 8% tangled, not in itself a cause for alarm. Finally, a couple of jars have become mutually dependent (caused by a light dependency from jstl.jar to standard.jar) – easily fixed, not a problem.
Class level tangles confined to a single package are not necessarily a problem. It is when a class-level tangle spans multiple packages that it really becomes a problem since this causes a package-level tangle. So when we look at the class tangles, we look down the “#parents” column:
The tangle of 32 classes spread across 6 packages is likely to be more of a concern than the tangle of 67 spread over 2 (though neither are good). The tangles that are confined to a single package are generally ok, though I’d maybe take a look at that tangle of 19 and see if it is justified. Taking a look at the tangle at the top of the list:
Obviously a suitable tool is needed to make sense of this graph, but even from a distance it is clear that the tangle is indeed a big complex beast that is not going to be the simplest inter-dependent set of classes to extend and maintain. It is not necessary that classes be completely acyclic, only that tangles are constrained or reduced so that they can fit in a single package while not making that package overly complex. Apart from its size (it’s too big to place in a single package), the problem with this tangle is that is spread across 6 packages, creating a package-level tangle:
(The orange boxes are packages). In this case the tangle does not stop at the first level of packaging. The design hierarchy pulls in several other packages (e.g. taglib.nested.bean, tiles.actions, …) into the tangle as it percolates right up to org.apache.struts:
Because it makes a big chunk of the code-base highly inter-dependant, I would focus on this issue first to improve the structure (and, more importantly, the productivity of the developers!). Good news is that it seems reasonably “finite” – not like some code-bases where it’s hard to find anywhere to get started. For example, in the diagram above, there are a few light weight dependencies (1 or 2 references) that will probably be easy to remove or reverse. In the class diagram grouped by parent, there were 2 packages that contain just one class from the tangle – moving those classes into one of the other packages could remove their current packages from the tangle. There are also the other package tangles to investigate – though these are smaller and/or more localized.
From the over-complexity perspective (other than tangles), there are a number of big classes, especially around the parsing area (parsers and GUIs are always ugly!) and a bunch of complex methods. These are a low percentage of the code, and relatively insignificant compared to the big tangle:
|Metric (and scope)||Threshold||#Offenders||Offenses (%)||XS contribution|
|Tangled (design)||0||11 of 29||38%||39%|
|Fat (design)||60||0 of 29||0%||0%|
|Fat (leaf package)||120||2 of 82||2%||17%|
|Fat (class)||120||23 of 1,094||2%||17%|
|Fat (method)||15||127 of 9,889||1%||27%|
I would maybe take a look at the more complex (“Fat”) classes and methods and if any were undergoing a high rate of change I’d prioritize them for refactoring.
I had lots of feedback, generally positive, on how easy Spring was to work with and extend. I’m not at all saying that Struts is a disaster, but I would expect it to be more difficult to work on, particularly in a team environment. In as much as this can be quantified, I’d love to get any feedback from Struts developers as to how they find its “developability”.