This article will explain how to maximize the benefits of using Sonargraph in day to day development operations. One major benefit is to stop and reverse architectural decay, but Sonargraph can also help to limit complexity and find actual problems in the code.
Location of Sonargraph System Folder
Make sure you add your Sonargraph system folder to your version control system. The system folder is created when you use Sonargraph to create a new software system. The first thing you are asked by Sonargraph beside the software system name is the location where you want to store the Sonargraph model. The model is stored in a folder that we call the “Sonargraph system folder”. This folder must be located within your project root or beneath. I recommend adding a ‘sonargraph’ folder to your system root or put the system folder right into your system root directory. All the files within this folders are text files and comprise the different aspects of your Sonargraph model. By distributing them over your version control system you can ensure that everybody in your team and your build server are using the same Sonargraph model. If you make changes to your model you should also commit those changes to the VCS. Your team mates will retrieve tose changes by pulling changes from your VCS.
Workspace Setup
Before we start with defining the rules it is critical to spend some time with the right setup of Sonargraph on your project. In particular we want to let Sonargraph know which code is generated and separate between test code and production code. This has to be done in the workspace view:
There are 3 filters in the workspace view:
- The workspace filter can be used to completely exclude certain files from being parsed by Sonargraph. That is mostly useful for languages with long parsing times like C/C++.
- The production code filter can be used to exclude test code from the analysis. In contrast to the workspace filter the excluded code is still part of the model but is treated as excluded. This is useful when you want to find dependencies from production code to test code.
- The issue filter is mostly used to exclude certain parts of your code base from generating Sonargraph issues and is mostly used for generated code. If you do not control the code generator there is not much you can do about issues in generated code. You can also exclude legacy code that is not going to change anymore and works well enough. Please note that the code filtered out by the issue filter can still generate architecture violations.
Eight Golden Rules
After you verified the correct setup we can now start with defining our first rule and most important rule:
(1) All rules must be checked in an automated way
Automation is critical. If you do not automate you need somebody to check the rules manually and then notify the people who broke the rules – a very unpopular task. Just integrate Sonargraph into your CI build and break the build if rules are violated. Or even better, if you happen to use Java you can use our IDE plugins for Eclipse and IntelliJ and get error markers on the code even before committing changes.
Architectural Rules
(2) Create an enforceable architectural blueprint
Most people that start with Sonargraph already have an existing code base that has some sort of architectural rules and patterns. In that case I’d recommend to create an initial model using Sonargraph’s architectural view. The third video on our “take a tour” page describes how to start with that. Please note that in the video we talk about the “Session View” which has been renamed to “Architectural View” in the meantime. (Apologies, but it shows that we constantly work to improve the product. The renaming was proposed by one of our power users). The architectural view can also be used to simulate refactorings like moving or renaming classes.
If you do not have a code base yet you can directly start specifying your architecture using our architecture DSL (domain specific language). The second video on our “take a tour” page describes how to do that. There are also quite a few articles on that blog site that give you a good introduction into the DSL, e.g, “How to organize your code”.
If you have little time and want quick results you can make use of our “Architecture Angels” service, where we use online screen sharing to support you with the definition of the model and other aspects of code quality and architecture.
Having an enforceable model will already guarantee that your system will not end up as the dreaded big ball of mud, as shown in the screen shot below:
Cyclical dependencies are harmful
(3) Avoid cyclical dependencies
Architectural blueprints divide your system into smaller and more manageable components. Sonargraph will ensure that the architectural model is free of cyclical dependencies. Therefore it is not possible anymore to end up with an unorganized mess as shown above. But it is still possible to have cyclical dependencies within the architectural elements. There are many books that describe in detail why cyclical dependencies are a bad thing. For starters: they make testing and code comprehension much harder and inhibit the reuse of code. Sonargraph can break the build if cycle groups grow over a certain size. For new systems I would recommend to completely forbid cycles between namespaces/packages and limit class cycles to 5 elements at most.
To configure the critical cycle group size you open the system configuration dialog (System/Configure…) and then select the respective configuration parameter:
Component cycles refer to cycles between “components”. In Java, C# and Python a “component” is a single source file, in C/C++ a component comprises header and source files that form belong together logically.
Sometimes cyclical dependencies are introduced between domain classes by ORM mappers or similar technologies. In that case you should identify those cycles in the Sonargraph cycle groups view and right click on them to ignore them. This means that Sonargraph will not consider them as harmful issues. Just make sure that those cycle groups are only comprised of domain classes. If at a later point in time other classes are added to the cycle group Sonargraph will create an issue and break the build.
If Sonargraph finds some cyclic dependencies in your existing code you can use the cycle breakup view to get some proposals how to break up the cycles.
Limiting Complexity and Size
The most important quality attribute of code is that it must be readable. Developers spend a lot of time reading code. If code is hard to understand that time increases significantly which leaves less time for changing or adding code. There are several metrics to measure complexity. Nesting depth measures block indentation, the deeper the nesting the more difficult it is to understand the code. Cyclomatic complexity measures the number of different paths that could be taken to execute a method or function and is also the lower boundary for the minimum number of test cases needed to reach 100% test coverage for a function or method.
I usually recommend the following complexity thresholds:
(4) Maximum block nesting depth must be less than 5
(5) Modified cyclomatic complexity must be 15 or less
Although cyclomatic complexity (CCN) is a well researched metric, nesting depth seems to be an even better predictor of excess complexity. Many tools use much lower thresholds for CCN which I do not agree with. Experience shows that low thresholds lead to artificially splitting up blocks of code that belong together, which in turn harms readability. From research we know that error probabilities increase sharply if CCN is 25 or higher.
It is also wise to limit the size of source files:
(6) No source files should have more than 900 lines of code
This threshold can obviously be adjusted to specific situations. The goal is to make sure to avoid monster classes with too much responsibility. Those classes also easily turn into bottlenecks causing a lot of unwanted cyclical and non-cyclical dependencies.
Overall code structure
Recently we added a new metric “Maintainability Level” to Sonargraph that measures overall structural health of modules. This metric can be used as an early warning indicator if a module becomes too tightly coupled. If all other rules are followed it is not very likely that the threshold will be triggered, so I would consider the following rules as a safety net:
(7) Maintainability Level must not be lower than 75%
Code Smells
Sonargraph comes with a scripting engine that allows searching for very common or even very specific code smells. A very undesirable code smell are classes that depend on their subclasses. Sonargraph comes with a script that can check for that in an automated way. Therefore I highly recommend to activate that script. To do so you must import one of the quality models that come with Sonargraph and then make sure that the script is activated in the script runner configuration of the system configuration dialog.
(8) No class should have dependencies to its subclasses
If you do not allow any class cycles that script does not need to be activated because a supertype to subtype dependency also always creates a class cycle.
Allowing Exceptions
Sometimes a rules violation can or even must be tolerated for various reasons. In that case just right click on the violation in the Sonargraph issues view and select “ignore” from the context menu. this is also the right strategy to deal with violations you are not able to address at the moment. Just make sure that you reserve some time in the future to address those issues.
Build Integration
Now all that is left to do is to configure the automated build and the fail set that will break the build. I recommend to use Gradle or Maven to call the Sonargraph build integration, even if you are not using Java. This will enable automated downloads and even automated updates of Sonargraph-Build. Of course it is also possible to run Sonargraph-Build without any third party build tool.
Here is an example fail set for Gradle:
failSet
{
failOnEmptyWorkspace = true
include(issueType: "any", severity: "error")
include(issueType: "Supertype uses subtype")
exclude(issueType: "ScriptCompilationError")
}
Now the build will fail with any error, which are caused by architecture violations, critical cycle groups, metric threshold violations that are configured go be errors and our script error.
If you use those rules consequently on your project I can promise you that it will be better than 90% of all other projects of similar size and complexity. As always, please feel free to leave comments below.