Code smells occur at all granularities. We may categorize smells based on their scope and impact. Specifically, smells arising within a local scope, typically within a method, could be referred to as implementation smells (such as empty catch block or magic number). Smells that involve properties of a class and the scope of impact comprises a set of classes then they are referred to as design smells (such as god class and multifaceted abstraction). Smells arising at the highest granularity, where we observe the properties of components (packages/namespaces) and their influence on the whole system, are referred to as architecture smells (such as god component and dense structure).
The architecture of a software system represents the critical design decisions that span multiple components and have a system-level impact. Therefore, architecture smells affect a set of components and given its relatively wider scope require considerable effort to refactor. Though the risk and effort to refactor increases with the increase in the granularity of smells, their benefits also increase significantly. In this context, it is relevant to understand the evolution of architecture quality. The evolution may reveal interesting insights about architecture quality of the software project.
This article provides a summary of architecture smells with examples and presents a case study of analyzing the evolution of architecture quality using DesigniteJava and an open-source utility program. You may use the open-source utility to carry out a similar analysis for your Java project.
Software engineering literature has documented numerous architecture smells; a detailed list can be found here. Let us understand some of the architecture smells that we can detect in a software system.
- Ambiguous Interface: This smell arises when a component offers only a single, general entry-point into the component.
- Cyclic Dependency: This smell arises when two or more architecture components depend on each other directly or indirectly.
- Dense Structure: This smell arises when components have excessive and dense dependencies without any particular structure.
- Feature Concentration: This smell occurs when a component realizes more than one architectural concern/feature.
- God Component: This smell occurs when a component is excessively large either in the terms of LOC or number of classes.
- Scattered Functionality: This smell arises when multiple components are responsible for realizing the same high-level concern.
- Unstable Dependency: This smell arises when a component depends on other components that are less stable than itself.
Let us take an open-source project to analyze and see some examples of architecture smells. We analyzed the latest version of Jenkins using DesigniteJava Enterprise. The analyzed Jenkins version contains 203 thousand LOC.
We found many instances of god component architecture smell.
A large component is difficult to understand and thus harder to maintain.
Many such instances reduce the maintainability of the project.
One of the instances reported in package
this package contains 646 classes and total 56,710 LOC.
The tool also reports an instance of cyclic dependency smell.
This instance contains a long chain of dependencies forming a long tangle.
The involved components are
hudson.init, hudson.util, hudson, jenkins.util.io, hudson.model, jenkins.model,
hudson.security, hudson.model.queue, jenkins.security, hudson.tasks, hudson.model.listeners,
hudson.security.captcha, hudson.diagnosis, jenkins.util, jenkins.model.lazy, hudson.widgets, hudson.scm,
hudson.util.io, jenkins, hudson.slaves, jenkins.slaves, hudson.node_monitors, hudson.console,
It is important to realize that unit cycles (two components depend on each other) are relatively easier to find by a
However, cycles of length 3 or more are subtly hidden in your code and you need tool support to help you reveal such
A feature concentration architecture smell occurs when a component is realizing more than one architectural
In other words, the component is not cohesive.
Akin to LCOM (Lack of Cohesion of Methods) that applies to classes,
DesigniteJava computes LCC (Lack of Component Cohesion) to measure the component cohesion.
jenkins.util.io suffers from this smell where computed LCC is 0.7.
Independent sets of related classes within this component are:
[PathRemoverTest, FileLockerRule, PathRemover,
PathChecker], [FileBoolean], [LinesStream], [CompositeIOException], [RetryStrategy], [PausingGCRetryStrategy],
Scattered functionality indicates that two or more namespaces are realizing the same architectural concern
(kind of opposite to the feature concentration smell).
DesigniteJava checks access to external namespaces that occur together from a method.
If such accesses happen many times in a component, it leads to a scattered functionality architecture smell
in the accessed components.
One of the 18 examples in the analyzed code shows that
are accessed together frequently and are realizing similar functionality.
The last example of architecture smell belongs to dense structure. This smell occurs when all the components form a very dense dependency graph. Thus, at most only one instance can occur per analysis. DesigniteJava forms a dependency graph among all the packages and computes the average degree of the graph. For the analyzed version of Jenkins code, the tool reported an average degree = 9.02 that implies that each component is associated with more than 9 other components on an average which is quite large. It indicates that coupling among components is high and efforts must be dedicated to reducing it.
Analyzing the evolution of architecture quality
We develop a utility program in Python to analyze multiple versions of a Java project and detect architecture smells using DesigniteJava. This utility program uses the analysis reports generated by DesigniteJava and then produces evolution data (LOC and number of architecture smells in each version) and plots (architecture smell density, version-wise architecture smell distribution, and component dependency structure) to present evolution. This utility program is open-source and you may use it to observe the evolution of architecture quality of your Java projects.
Here is the result of analyzing 10 versions of Jenkins. The utility program summarizes each analyzed version including total LOC and number of architecture smells found.
|Version||Commit-hash||Date||LOC||Total architecture smells|
Comparing absolute values of detected architecture smell instances across versions is not a good idea because the size of the code is also changing. Therefore, we compute smell density. It is a normalized metric capturing number of smells per one thousand LOC. The utility program produces the plot as follows.
It shows that architecture quality was deteriorating till V6 and then started improving. It could be the result of conscious focus on architecture quality and refactoring.
We are also interested in fine-grain details. The utility program generates smell-wise summary too so that we can observe the contribution of individual architecture smells per version towards quality degradation. The generated summary is presented below.
|Version||Ambigious interface||Cyclic dependency||Dense structure||God component||Feature concentration||Scattered functionality||Unstable dependency|
Graphically, we can observe the same information in the form of a stacked-bar plot. We can observe that feature concentration smell instances are the most frequently occurring architecture smell in Jenkins followed by unstable dependency.
Finally, we can see how the overall structure of the software getting complex in each analyzed version. The utility produces the component dependency graph for each analyzed version where dense structure smell is detected. In these plots, each component is represented by a graph node and size is proportional to their degree. These plots also show five components with the highest degree (number of dependencies to other packages).