Using NDepend to improve my Application Code
01/18/2024
7 minutes
NDepend is a .NET code quality tool that performs static code analysis. It can help to detect code smells and dependency issues. In this post I will take a quick look at using the Dependency Graph feature to clean up one of my open-source applications. NDepend has a desktop application (as well as Visual Studio extension) that can be used by developers in a tight development cycle to address dependency and code quality issues during development:
change code
build application
run analysis (or configure automatic analysis in NDepend for every build)
The tool also comes with a handy console application that can be leveraged for enforce quality rules on CI/CD agents. Later in this post I look at generating reports with the NDepend Console on build agents.
Dependency Graph
Dependency Graph is a tool that can visualize dependencies of application components and identify common issues, such as cycles. I use this tool to re-organize C# classes and to fix namespace declarations in my project. To get started I set up an NDepend project using the desktop application VisualNDepend.exe. In the project properties select the dll-s to be included in the project analysis.
Note that the NDepend project file can be submitted to source control, so that a common rule set can be shared across the development team.
I leave the rest of the settings as default, and I use the green play button to Run Analysis. The initial analysis shows a dashboard with issues, quality gate violations and debt. In a tight development cycle a developer can track the trend of metrics or compare them with a known baseline. NDepend stores a single report for each day that is used for visualizing long term trends.
Navigate to Dependency Graph tab to visualize the application map:
It is instantly visible that my dependencies are less than ideal. The red edges with an arrow on both ends indicate cycles in across application components. One of the edges indicate that CHttp.Abstractions should not reference the CHttp.Statistics:
At this point I could search all classes to explore which cause dependency cycle. However, there is a quicker way with NDepend. Right click on the edge and select Open this dependency on Matrix. This opens a Structure Matrix with the types in the scope of the target namespaces:
The blue and green squares show the dependencies between classes. It is immediately visible that the green squares indicate the undesired dependencies.
In the next step I refactor the code and repeat the above cycle. After many iterations I resolve all dependency violations resulting a conflict free dependency graph:
During this process I realized a few ideas helped to become more productive:
Instead of jumping on the first dependency violation it is better to first outline the desired application structure. Then iterate the analysis, build, refactoring steps until the wanted architecture is achieved.
The tool performs a static analysis on the build output of the C# compiler. This means that unused using statements or trimmed code might not be reflected on the diagram.
Setting up Code Coverage analysis
Next step could be setting up code coverage analysis. In the desktop NDepend application a developer can add coverage files in Project Properties -> Analysis -> Code Coverage -> Settings window. NDepend handles opencover coverage format.
To setup Open Cover I add the coverage.collector nuget package. Then create a runsettings file, that specifies the format to be opencover and defines the excluded files:
<?xml version="1.0" encoding="utf-8" ?> <RunSettings> <DataCollectionRunSettings> <DataCollectors> <DataCollector friendlyName="XPlat Code Coverage"> <Configuration> <Format>opencover</Format> <ExcludeByFile>**/test/**/*.cs</ExcludeByFile> </Configuration> </DataCollector> </DataCollectors> </DataCollectionRunSettings> </RunSettings>
Run dotnet test command passing the runsettings file as settings to execute the tests and generate the test coverage:
dotnet test --settings mysettings.runsettings
The above command prints the test results and the test coverage file path to the console:
Passed! - Failed: 0, Passed: 97, Skipped: 0, Total: 97, Duration: 7 s - CHttp.Tests.dll (net8.0) Attachments: D:\repos\Http3Tools\tests\CHttp.Tests\TestResults\9e0687e7-8f5b-4673-a684-0cc969035a29\coverage.opencover.xml
The filename reflects that the generated report is indeed in opencover format. The containing folder path with file filter should be specified in the NDepend project. Re-running the analysis includes the Coverage report on the dashboard:
NDepend Reports
When selecting the Run Analysis and Build Report command in the desktop application, a set of HTML report files are generated. These reports can be viewed in a browser and hosted as static content on server.
A neat feature of these report files is that under issues tab one can navigate to the individual files to explore issues commented on the source code including the code coverage. It does not require a direct link to the source code, as these files are generated during the analysis.
NDepend Console
A careful reader might have realized that the HMTL file reports could be generated and shared by a build machine, so that a development team can review and act-up on them for every pull-request. However, the desktop NDepend application is a .NET Framework application that cannot run on a Linux build machine. Fortunately, NDepend comes with a cross-platform console application that can be used for the purpose. After registering the build machine license with the /RegLic
switch a Continuous Integration (CI) job can be extended to invoke the console application to generate the reports for a given NDepend project:
dotnet NDepend.Console.MultiOS.dll D:\repos\Http3Tools\ndepend\NDepend.ndproj /ViewReport
The static report files can be typically served by the build agent as artifacts. There is no need for setting up and maintaining separate code quality services. Even for the long-term trend reports only a shared directory is required, which can be configured with the /TrendStoreDir
switch. The CI pipeline by default enforces the NDepend quality gates, but if the goal is only to generate the reports, the /ForceReturnZeroExitCode
switch can be used to return sucessful exit code from the CLI command.
The primary purpose of the console application seems to be providing a cross-platform way to generate reports on build machines. However, it could be also integrated with a custom VS Code build task. A developer could directly integrate it with the build process via the tasks.json file, setting up a tight development cycle similar to one provided with the Visual Studio extension.
While currently NDepend generates comprehensive reports, a nice addition could be incorporating the Roslyn Analyzer warnings. Fortunately, this is already in the backlog of the future features for the tool.