Most dependency analysis for C++ involves the analysis of include dependencies to improve compile time. While this is a great start for analyzing dependencies, the goal should be to lower testing and maintenance costs. The way to do that is to make your software more modular. Most software architectures start out clean and with good modularity. Over time, architectural erosion happens. Quality declines and maintenance becomes more expensive because of poorly managed dependencies.

Architectural dependency analysis is a way to fight poor dependencies while minimizing test and maintenance costs. Dependency analysis creates a map of how different parts of a system affect one another and how various parts of a system require other parts to operate properly. Bad dependencies often arise when quick, short-term fixes are made that violate the system’s architectural rules. These dependencies become part of the technical debt.

Refactoring Dependencies

The first step in refactoring dependencies is to analyze and visualize the current dependencies in a system. Here are some things to consider when analyzing dependencies:

    • Change impact, or impact analysis, is about understanding what will happen when you make a change. Changes tend to have large, unforeseen ripple effects because of hidden dependencies. Impact analysis identifies all the files, packages, components, classes, etc. that are affected if you were to implement a change. Impact analysis can give you an idea of the development and testing costs of that change. The cost to change a class might be small, for example, but the cost of testing the change could be significant. This is especially true in safety-critical industries like aerospace and defense, healthcare, and automotive where testing costs are significant. These industries also tend to have large C++ codebases. Watch this video on how Lattix products perform impact analysis.
    • Metrics provide insight into bad dependencies that need to be removed. Metrics correlate components and their relationships with numerical values that express quality. Coupling, cohesion, and stability are good dependency-related metrics. System stability is a particularly good metric to track because it is a measure of the percentage of modules that would not be affected by a change to any module of the system. This gives you a general idea of your modularity.
    • The visual representation of dependencies can help focus on areas of bad dependencies such as cyclical dependencies or high complexity. It is an easy way to determine if design rules are being followed. Visualization has been used to correlate the dependencies of a system with the risk of defects1. Visualization also provides insight into modularity and quality of a software program. The goal is to provide an early warning system to detect future costs and risks. A great way to visualize dependencies is a Dependency Structure Matrix (DSM), which maps dependencies between modules in a system.

Dependency Structure Matrix for Architectural Refactoring

Once dependencies have been analyzed and visualized using a tool like Lattix Architect, it is time to refactor the code and deal with any unwanted dependencies. The two questions in refactoring dependencies are:

  • Which dependencies should be removed?
  • How can these dependencies be removed?

See our video on Architectural Refactoring for more details on how to refactor dependencies and perform what-if analysis.

Architectural refactoring can be difficult and should be done with great care. It is not always straightforward how things will work after refactoring has been performed. After each refactoring, the following checks should be performed:

  • Check that the program still compiles and builds
  • Check to make sure the program works as it did before the refactoring (unit tests, etc.)
  • Check that the architecture improved (metrics)

Summary

The business goals for dependency analysis and refactoring are to lower testing and maintenance costs. The attributes of this are: 1) testability, where a reduced module size makes it easier to test; 2) safety, where a more modular software reduces the impact of change; and 3) modifiability, where a clean, modular architecture is easier to understand and update. For help analyzing and visualizing dependencies in your C/C++, Java, or C# codebases, check out Lattix Architect.

1. Product Architecture and Quality: A Study of Open-Sourced Software Development