Yesterday I attended a NEJUG meeting presented by Alexander von Zitzewitz called "
Java Architecture Management or how to avoid the structural erosion of your code base". It was a pretty entertaining talk where he reviewed a number of scenarios that lead to the creation of unmaintainable complexity. He confirmed a lot of what I already knew, but nicely formulated and explained. He argued that no documentation is going to help you, it's prohibitively expensive to create and always outdated by the latest code. A general theme was that you need to have an architecture that slices horizontally (architectural layers) as well as vertically (functional slices). Think of vertical slices as your java package naming and how packages end up in a jar. This way you end up with a grid of subsystems. Next you can start analyzing the dependencies between the subsystems. He described a number of code metrics that can be used to quantify the number of architecture violations in the code. He came up with a list of 6 rules that one should follow to write maintainable code:
Rule 1: Define a cycle free logical architecture down to the level of subsystems and a strict and consistent package naming convention
Rule 2: Do not allow cyclic dependencies between different packages
Rule 3: Keep the relative ACD low (< 7% for 500 compilation units, NCCD < 6)
Rule 4: Limit the size of Java files (700 LOC is a reasonable value)
Rule 5: Limit the cyclomatic complexity of methods (e.g. 15)
Rule 6: Limit the size of a Java package (e.g. less than 50 types)
To break a dependency cycle you can should introduce an interface; this reverses the dependency. It is that simple. This leads to the observation that "good code" can be recognized using certain metrics (see also a white paper by
Robert Martin. Good code should have a certain ration of abstractness (classes vs interfaces), and if more code depends on your code, you will likely need more interfaces. The two extremes of this "metric distance" from the optimal ratio, are:
zone of uselessness which means a high number of interfaces and few things depending on you. This happens when coders plan for complexity that never happened.
zone of pain which means very few interfaces and lots of code depends on you. So each change in your class can break lots of other things elsewhere. This usually happens when people cut corners to make a deadline.
To monitor this adherence to the rules you need a tool. Commercially he listed three;
Lattix here in Massachusetts, Structure101 and
SonarJ, which is his product and which was later demoed. I was actually involved in a case study with Lattix a few years ago, which was interesting but felt like moving the chair on the deck of the Titanic. The case study only took into account the Java side and our major issue was the duplication of code on the server (Java) and the client side (Javascript). Code duplication is something that is not easily picked up and certainly not when it appears in the different Languages. We were simply to deep into our own poo to fix things. I think a rewrite was the only option for that product. Anyway I still learned a lot from it. For one that it is never too early to hook up a static code analyzer to the nightly build. If you register with hello2morrow you can download a free version of
SonarJ meant for projects with fewer then 500 Java classes. If you want to go Open Source then you can try:
PMD,
jDepend (dependencies only) and
Architecture Rules (on top of jDepend).
The meeting really confirmed my own experiences, that the process of creating good code is simple. You just have to follow it *all the f***ing time* (much like
Bryan Liles' TATFT framework). Bad code is like a black hole attracting other bad code. So simply don't put black holes in your code! If your system gets large, make sure you use tools to monitor that none of these simple rules are broken. Hook them up to the nightly build and have any violations be fixed the very next day. Note that you need an architecture before you can use any of these tools!
--Kurt