The search for structural excellence among the the most popular, open-source, Java programs continues with an analysis of Apache Struts, the much-loved web framework that, " ... works well with conventional REST applications and with nouveau technologies like SOAP and AJAX."
As usual, we shall look at each release's package-structure as a spoiklin diagram in which a circle will represent a package, straight lines will represent dependencies from packages drawn above to those below and curved lines will represent dependencies from packages drawn below to those above. The colour of a package will indicate the relative number package-dependency transitive dependencies in which it partakes: the more red, the more transitive dependencies.
Figure 1: Struts version 1.0.2.
Figure 1 shows the package-structure of Struts version 1.0.2 and with it our series of structure analyses gives birth to its first truism: programs begin well-structured.
This is not a vacuous statement. Circumstances need not have been so. What factors, then, bring it about?
Successful programs accrue features - and hence grow in size - as they age so young programs are small. Small stature, however, does not necessarily imply well-structuredness: we can make a mess of anything. The above figure might have presented those floating packages -
logic, etc. - mangled horribly, inextricably together. But it did not. The figure (ignoring a bow or two) presents a constellation of packages whose relationships with one another could hardly be clearer.
Perhaps, then, the very presence of legacy code - however well-structured - places unbearable constraints on battle-fatigued programmers, forcing them to abandon their principles.
But we are getting ahead of ourselves.
Figure 2: Struts version 1.1.
Version 1.1 adds 700 functions and the number of packages rises from 11 to 26. A programmer might consider the resulting diagram neither pretty nor ugly. Certainly the, "Upper half," seems satisfactory with most of these packages enjoying straight-forward relationships with their surrounds.
util, a self-proclaimed utilities package, must ever accept its role as dependency-sink and so a certain dependency clutter may be excused in its vicinity; the same is true of the sinky
action appears to provide sufficient difficulty in tracing ripple-effect changes to reduce the overall harmony of this graphic.
Nevertheless, the structure seems, overall, OK.
One great praise we might heap on Struts concerns its handling of circular function-dependencies: from this version onwards the number of these circular dependencies never spikes above 28, which is remarkably good in a system of (a fairly consistent) 7000 transitive dependencies.
Figure 3: Struts version 1.2.4.
Version 1.2.4. loses a package but otherwise structural tensions appear similar to the previous version.
Figure 4: Struts version 1.3.5.
High above package-level, sweeping changes in version 1.3.5. see its jar-structure torn apart. Whereas previous versions had bulged within the single
struts.jar file, figure 4 shows the contents of the
struts-core.jar alone. Some might raise concerns about comparing like-with-like, concerns countered by the claim that this analysis focuses not on Struts in its entirety but on its core components alone.
That being said, the new core-components jar holds 1000 fewer functions and 10 fewer packages than version 1.2.4. as is evidenced by the pleasing simplification of its diagram. A new
contexts package has unfortunately thrown itself into the fisticuffs of center-stage but overall structural qualities seem undiminished.
Figure 5: Struts version 2.0.5.
Struts2 - versions of Struts higher than 2.0 - presents yet another interpretation challenge: for is Struts2 structurally a derivation of the first major revision of Struts or an entirely independent product? Unfortunately, we might have to conclude that the latter holds than the former but the analysis shall continue, presuming (perhaps foolishly) some continuity.
This new version, then, brings with it an extra 1000 functions and does so gracelessly. Tracing ripple-effects through those central
dispatcher packages looks deeply problematic, the
components package alone generating almost 20% of the system's potential coupling.
All is not well.
Figure 6: Struts version 2.0.11.
Figure 6 emphasizes the problem with the
dispatcher package, namely the orgy of mutual bloody stabbings in which it engages with
multipart. The system has fallen to its lowest lawless level, structurally-speaking.
Figure 7: Struts version 2.1.6.
Figure 7 offers something of a surprise: a noted structural improvement in a mature product.
Make no mistake: version 2.1.6. remains a rather tricky system in which to trace dependencies but it at least improves on its predecessor. This appears, however, largely due to version 2.1.6.'s having ejected 700 functions and 8 packages, a material loss caused presumably by sub-surface jar-file tectonics.
In other words, the improvement on display is probably thanks to packages' having been shipped out of our analysis scope. We chalk this one up to good jar-file husbandry.
Figure 8: Struts version 2.3.8.
Little significant happens between version 2.1.6. and the final version in figure 8 (the software accommodates two further packages and accommodates them well). The system comes to rest at a rather intermediate structural state: superior to version 2.0.11. yet inferior to version 1.0.2.
As usual, no diagram presented here offers conclusive proof of structural value or cost; a high-level analysis can only ever prompt questions whose answers can be found solely through deeper inspection.
Yet however we appraise the final configuration of Struts in figure 8, one point remains: the structure begs many questions. Mutual dependencies abound: how many might prove unjustifiable even after further investigation? And if unjustifiable, how many could have been prevented in the first place?
To put this even more broadly: why was this system - and all systems analyzed so far in this series - allowed to descend to states of such questionable structural sub-optimality?
One answer may lie in the software industry's view of structure itself.
The history of software development resembles that of art in that grand trends tend to buck and veer directed not by institutions or overarching organizations but by individuals. The many follow the few, the opinions of these few holding tremendous sway.
When presented with this series' structural analysis of his excellent JUnit program, the matchless Kent Beck wrote, "I appreciate aesthetics in code, but somehow package dependencies have never triggered my OCD. In any case, JUnit 5 is an API release. Repackaging should be decoupled into either 4.12 or 5.1."
The most important point of this quote is that the good Mister Beck had already planned a re-structuring ("repackaging"): the unswerving dedication to cost-effective improvement marks all great programmers.
Slightly troubling, however, is that word, "Aesthetics," a word which carries with it an air of the frivolous, of the non-essential adornment. New programmers barging enthusiastically into our field might hear established titans make such utterances and conclude that structure is nothing more than aesthetics and so unworthy of intellectual or commercial pursuit.
This is unfortunate.
Yes, the execution of a program governs its user-experience but it is structure that governs its cost of change. It is structure that helps or hinders expensive weekend-consuming bug-hunts. It is structure that underwrites or undermines that desperately-needed feature upgrade. Structure is time. Structure is commercial survivability.
Furthermore, quotes from code-authors inform less than the code itself. A programmer's code definitively broadcasts cherished values. Open-source code, for better or worse, offers a model for novitiates to copy, a goal to which to aspire. When popular, open-source projects display poor structure they carry no implicit apology, we hear no, "This is below-par but we'll get around to fixing it sometime." On the contrary, that the code is released for public scrutiny shouts clearly, "This is how code should be written! This is how you, too, should structure your code!"
Though it strikes as unfair, open-source projects must be held to even higher structural standards than closed-source projects precisely because in making themselves generally available they implicitly claim to embody industry-held principles.
The structure of the Struts core components takes top position on the structure leader-board above JUnit but still falls short of recommendation.
Few would argue that testing has triggered something of a revolution in software development during the previous decade or so. Software has always been tested, but never in its history has software development sung the praise of testing quite as much as it sings now.
A structure revolution, however, seems far, far away.
|Absolute potential couple efficiency %||20|
|100 - (% length of longest dependency that half the system is shorter than)||70|
|% Method transitive dependencies spanning 2 packages or fewer||86|
|((25 - (number of transitive method dependencies per method) / 25) as % of 25||90|
Table 1: Struts 2.3.8's structural evaluation.