Architecture Smells - Detection And Refactoring

The concept of “smells” has been familiar within the software engineering community for almost the last two decades. The term “code smell” first appeared in the popular Refactoring book by Martin Fowler; it was informally defined as “certain structures in the code that suggest (sometimes they scream for) the possibility of refactoring”. An excessive number of smells in a software system impairs the quality of the software and makes the software hard to maintain and evolve.

We can perceive smells and their corresponding refactorings at various granularities. Smells could be categorized as implementation smells, design smells, and architecture smells based on the scope of the impact made by the smells.

Among these types of smells, architecture smells’ impact and scope are the highest. It implies that the consequences of the architecture smells are felt in a wider scope within the software system and if we choose to refactor the smell, it will require much more effort than other kinds of smells (such as implementation smells). On the other hand, the benefits that an architecture smell refactoring would bring are also significantly higher. Thus, although, it is relatively difficult to carry out architecture refactoring, it is significantly fruitful and rewarding.

Architecture smells

Let us understand various architecture smells that may arise in a software system. Here, an architecture component is a namespace (or package in Java world).

  1. Cyclic Dependency: This smell arises when two or more architecture components depend on each other directly or indirectly.
  2. Unstable Dependency: This smell arises when a component depends on other components that are less stable than itself.
  3. Ambiguous Interface: This smell arises when a component offers only a single, general entry-point into the component.
  4. God Component: This smell occurs when a component is excessively large either in the terms of LOC or number of classes.
  5. Feature Concentration: This smell occurs when a component realizes more than one architectural concern/feature.
  6. Scattered Functionality: This smell arises when multiple components are responsible for realizing the same high-level concern.
  7. Dense Structure: This smell arises when components have excessive and dense dependencies without any particular structure.

An interested reader may look at a comprehensive catalog of architecture smells online.

Detecting Architecture Smells

There are some tools to identify architecture smells. Let us analyze an open-source C# project using Designite. I analyzed NRefactory which is an open-source library providing code parsing and semantics analysis for C# code. The tool reported that the analyzed project has more than 269 thousand lines of code and has almost two thousand types (classes, interfaces, etc.). The other details of the source code analysis are shown in the following figure provided by the tool.

Architecture

Let us look at the detected architecture smells by the tool. The tool has detected total 105 architecture smells. Scattered functionality occurs the most (46 instances) while there is no ambiguous interface architecture smells in the code. The following figure captures other detected architecture smells in the analyzed project.

Architecture

Approaching Architecture Smells’ Refactoring

For a development team, it is challenging to strategize refactoring of a set of architecture smells. A component may have many classes and it is not trivial which classes to touch in order to refactor the reported architecture smell in the component. To deal with this hurdle and to facilitate developers with the refactoring of architecture smells, Designite reports responsible classes for each architecture smell instance. It is a significant help that a development team is getting in the refactoring process saving their time and effort.

Let us understand how responsible classes associated with an architecture smell instance could be helpful with a couple of examples,

Example 1 – Cyclic Dependency

Two components are cyclically dependent on each other if some classes in component A depend on component B and vice versa. The tool reports a cyclic dependency smell instance as shown in the following figure,

Architecture

The above figure shows that namespaces ICSharpCode.NRefactory.Semantics and ICSharpCode.NRefactory.TypeSystem are dependent on each other. Furthermore, the tool also shows the responsible classes for the cyclic dependency. The tool reports that there are two classes (AmbiguousTypeResolveResult and ErrorResolveResult) from Semantics namespace access classes from TypeSystem namespace. On the other hand, three classes (ComHelper, DomRegion, and TypeSystemExtensions) from TypeSystem namespace uses classes from Semantics namespace.

This information saves developers time and effort to figure out what precisely leads to the architecture smell and provides an explicit help to refactor the architecture smell.

Example 2 – God Component

God Component indicates that the component is excessively large either in terms of LOC or number of classes. The tool detects 10 instances of God component architecture smell in the analyzed project. Let us examine one of the detected instances.

Architecture

As the above figure shows, the namespace ICSharpCode.NRefactory.CSharp has more than 38 thousand lines of code and thus the tool declared it as a God component. In addition, the tool reported four classes that are primarily responsible for this architecture smell - CSharpParser, ConversionVisitor, FormattingVisitor, and CSharpOutputVisitor. These are the largest classes in the component; for instance, class CSharpParser has more than 15 thousand lines of code. The tool helps to identify classes that the development team may target to refactor to get rid of this architecture smell.

Conclusion

Code quality is an important aspect that every development team must care about. Code quality issues may arise not only at the implementation level but also at the architecture granularity. Identifying smells at all granularities and refactoring them help a team maintain high code quality and thus enable high productivity.