Category - Tutorials

NDepend Demo

Using NDepend

Overview

During this tutorial we will explore NDepend, which is a code quality tool that integrates seamlessly with Visual Studio. The code rules are written using LINQ queries that are nested in an NDepend project file in the solution. This allows a shared code environment to share the rule queries.

To setup code to be ignored by the rules you can use the NDepend code attributes or even create your own custom attributes and change the rules to use them. In addition to the code rules, NDepend also comes with several interactive reports such as a Dependency Graph, Dependency Matrix and Metrics.

NDepend even creates an interactive html output and can be built into a Continous Integration process to stop the builds if critical rules are violated. The overall aim is to help create a code base that has cleaner dependencies, is more extensible and is more manageable.

NDepend is not a free product, but does come with a 14 day trial. Even for small development teams it can have a huge impact on the code base.

The code for the tutorial is located at NDepend Demo on GitHub. I'll be providing direct links to each step of code as I have flagged each step as a release. For example Download 1.0.0 or review the complete changes made in the commit for example Changes for 1.0.0 .

Let's get started!

Install NDepend

  1. Go to NDepend and download the 14 day trial. To install NDepend follow these steps:

    1. Extract the files from the zip.
    2. Create a folder in your root C:\ or really, wherever you want. I had folder called Tools in my C Drive from other applications I have installed in the past, so I created a folder at C:\Tools\NDepend
    3. Place all of the contents from the zip into the folder created in step 2.
    4. Double click on the NDepend.VisualStudioExtension.Installer.exe
    5. Choose the versions of Visual Studio you would like to install NDepend to.
    6. Now fire up Visual Studio. You will get a few prompts from NDepend in regards to the trial and agreement.
    7. Let's rock!

    You will notice a new menu item in the top menu named NDepend, and you will also have a circle in the very bottom right of visual studio that is likely grey at this time. The menu at the top will be the main spot to hunt for things you can do, while the icon in the bottom corner is activated on hover and gives you a status of the current project.

Setup Website

  1. To get started I have created a basic MVC5 application. I've included class libraries for a DAL and a Domain. This is to give us a light version of a more robust application.

    I suggest you create a folder on your computer at C:\Applications\NDependDemo and place the above website in that location. NDepend does use some absolute paths in it's project file, which I'll describe why when we get to that. It isn't hard to change though and will only affect you if you download version 2.0.0 or later of this demo.

    If you are following along with this tutorial from the start, you can place the site whereever you would like and as you add NDepend it will path to that location.

    The web application does have some nuget packages, so after downloading or forking the above link, but before you build the web application go to Tools -> NuGet Package Manager -> Package Manager Console and you should see a "Restore" button. Click that button to restore the nuget packages, then build, and run the application.

    The application uses entity framework code first and creates a database in the App_Data folder with a few items in it. The database will be created on the first data call, so click on the ToDos or ToDo Category link in the top menu. It will take a brief moment to load while Entity Framework creates the database.

Adding NDepend

  1. NDepend is attached in a solution, or can even be a separate project of it's own. We are going to attach it to the current project. So, with Visual Studio as the active application, hover over the icon in the bottom right of the screen (or click on the icon) to open Attach a new NDepend Proejct to VS Solution

    NDepend Attach New

    Click Analyze 3 .NET Assemblies. NDepend will run and inspect your code. While doing so you will see an Analysis Error List giving you a print out as it runs, and finally a window will open called NDepend Beginner, Please Read. For now click on View NDepend Dashboard

    NDepend Beginner

    NDepend will also open a browser window to the html output window. So much to do and so much to see!

    I updated the .gitignore and included the following line of code so that git repositories will ignore the NDependOut folder which is where the html report is generated to.

    If you wish to make the same change, just open the .gitignore file and add the following line at the end:

    NDependOut/*
    

Explore NDepend

  1. Dashboard

    NDepend Dashboard

    The dashboard is typically one of our first stops. There is well more in NDepend then I have time to show. However, hopefully I can take you through a few of the quick cool things and get into some guts of how NDepend will help your development.

    First off, it shows you the Code Rules section, which displays information like the number of Critical Rules Violated as well as Rules Violated. The critical rules are ones that could be set to halt a build process if they were violated.

    There's also a lot of other cool metrics like Comment percentage, # Types, and # Lines of Code. A lot of the metrics will be better managed by watching trends in future versions and there is some of that functionality here as noted by the light blue link at the top for Define a Baseline for Comparison. But let's look farther.

  2. Queries and Rules Explorer

    NDepend Critical Rules

    Click on the link for Critical Rules Violated and NDepend will open the Queries and Rules Explorer. This is where I spend most of my time with NDepend. On this page you can see we have violated 3 critical rules; Methods too complex, Potentially dead Methods, and Potentially dead Types.

    You can also browse through the project rules on the left. For example if you click Object Oriented Design, you will see we have 38 items that have violated the "Class with no descendant should be sealed if possible" rule. Or click on Visibility and you will see we have 179 violations of "Methods that could have a lower visibility".

    NDepend Queries Rules Edit Explorer

    In the steps following we will spend a lot of time with the rules customizing them and changing them. But let's look at some of the reporting features for right now.

  3. Dependency Graph

    Click on NDepend on the top menu and select Graph and then View Dependency Graph. This graph is excellent for helping to spot circular dependency issues or even just get a good overview of the application. One of the huge benefits of NDepend is that you can add assemblies from other solutions in as well. Allowing NDepend to not only explore the code in the current solution, but across your entire application library!

    NDepend Dependency Graph

    By hovering over the various items on this graph, you get some great metrics. You can also do an immense level of configuration by right-clicking over a node and checking out the holy-mary-context-menu-of-ultimate-options. This is great for allowing you to eliminate 3rd party libraries and just check out how your libraries interact with each other or vice versa.

  4. Code Metrics View

    Click on NDepend on the top menu and select Metric and then Code Metrics View.

    NDepend Code Metrics

    I usually enjoy seeing the strange faces people make when they look at heat maps and try to comprehend them. If you haven't seen a heat map before, I sure wish I could see your face right now...

    Basically, the size of the box has to do with the number of lines, methods, and overall complexity of an area of your code base. The color coding indicates how many issues are there. For example in the middle right is ManageController which is a yellowish-green and fairly large box. It is a large controller with more than it's fair share of violations.

    Most applications that have been going for awhile have incurred some technical debt and will be far more colorful then this baby web application. Be sure to check out this report after you add NDepend to a larger application.

    There are a LOT more reports than I have time to go into, including ones for diff, and code coverage! But let's get to digging into the rules and see if we can get that red dot in the bottom right corner that implies that NDepend is not happy with our code to turn a different color! Maybe even... *gasp* yellow!

Managing Code

  1. Let's get back to the Queries an Rules Explorer. You can get there a few ways: Top Menu -> NDepend -> Rule -> View Explorer Panel or hover over the red dot in the corner and click any of the errors near the top of the modal window.

    Let's go after a big one to start with. Click on Visibility and click Methods that could have a lower visibility. This will open a window called Queries and Rules Edit.

    NDepend Queries Rules Edit Rule

    The top part of the window has all of the LINQ code that is run using reflection to inspect our code for matches. You can see it uses LINQ keywords by starting with the "from" and ending with a "select".

    There are a lot of comments that help you understand why they added this as a default rule and how to resolve it. Scroll down in the top part of the window until you get to the <Description> block.

    NDepend Queries Rules Edit Notes

    What you have to be careful of is that the intentions here is to help create a stronger application, but some changes can cause significant issues with problems outside of NDepends knowledge. For example we have 10 Methods that violate the rule in NDependTestWebsite. One of those is the BundleConfig, which can have a lower visibility. So in the App_Start/BundleConfig.cs file, change the signature of the class to be internal per below.

        public class BundleConfig
        {
            // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
            internal static void RegisterBundles(BundleCollection bundles)
            {
    

    To re-run the report, hover over the icon in the bottom right and choose Run Analysis and Build Report. When you go back to the Queries and Rules Edit window you will see that we now only have 9 methods matching.

    NDepend Methods Lower Visibility

    We could do try this for some of the other methods noticed in the AppStart folder, but we would need to extensively check the application afterwords. For example; NDepend reports 23 methods in the AccountController. One of those is the Login method on line 58. If you change the access to internal, the application will build, however, if you run it and click on the login link on the navbar, you will get a 404 error. That's because controller methods are entry points and must be public!

    Please make sure the login method is set back to public if you decided to have fun blowing up your site before we continue on ;)

Managing Rules

  1. Removing Code Rules

    One of the rules that I am not a big fan of is in the Object Oriented Design section called Class with no descendant should be sealed if possible. I work with a lot of libraries that interact with each other. If you have all of those linked together, NDepend will recognize if a class is extended in any of those. However, many times I'm writing code for the Open/Closed Principle which states that classes should be open for extension but closed for modification.

    Now an argument can be made for managing this rule through attributes which we will discuss in the next step, and would force us to pay attention to each violation. We would then flag each one individually to be disregarded by NDepend. However, I'm going to pick on this rule and use it as my example of removing a rule.

    It is extremely difficult..... Go to the Queries and Rules Explorer, click on Object Oriented Design, right click on "Class with no descendant should be sealed if possible" and choose delete. Click Yes... and you are done. Whew! That was tough.. /sarcasm off ;)

  2. Sharing Changes

    Now, you may be wondering... if we are using a shared code base, are the other developers going to all have to remove that rule as well? I'm so glad you asked! Open a file explorer and go to the root of our project. You will see a NDependTestWebsite.ndproj file.

    Open the file with Notepad or another text editor and let's see what we got.

    The first thing you might notice is the "OutputDir" is an absolute path, as well as in the "Dirs" section is another absolute path. This is needed because remember, NDepend can be used to pull in files from several solutions all into one NDepend file! The only way it can reliably deal with the paths to navigate to those solutions is using absolute paths. This does mean that if you do share this file between developers, they will need to have the same folder structure for your applications locally.

    A pro to share this file in source control can be seen by scrolling down. You will start to see a horde of comments and LINQ queries. NDepend adds all of the rules for this project in this file. If we change a rule and share this file through our source control, all of the other developers that have NDepend installed use the changed rule as well.

    However, this won't stop developers that do not have NDepend from using the application as their versions of Visual Studio that do not have NDepend installed will just ignore the ndproj file. :)

    You could choose to have the majority of your developers run the projects normally whereever they want and then just have your architects or code reviewers use NDepend in the same location as each other, and they can manage the fail cases.

Modifying a rule through Attributes

  1. View Potentially Dead Types

    Let's say there is a rule we do want to keep, but at the same time it's throwing a violation for a code block that should break the rule. We have two choices, we can tag the method or class with an attribute, or we can make changes to the query to ignore that class or method.

    Go to the Queries and Rules Explorer and select Dead Code and then Potentially dead Types. The issue it is finding is NDependTestWebsite.Migrations.Configuration which is the configuration for our Entity Framework migrations. If you are not familiar with code first migration check out my blog post on Code First EF with Web Api 2 . Suffice to say that our application will never directly call this class. We will call it after making changes by calling a command in the Package Manager Console. However, if we delete that file, the migration will not work.

    It would also be exceedingly annoying to comment out that class and then uncomment it when we need it.. so what else can we do? In the Queries and Rules Edit window check out the top part of the view.

    NDepend Dead Types

    There are actually instructions on how you can tell NDepend to ignore the Configuration class. We could add a reference to NDepend.API.dll and then tag the class with the [IsNotDeadCode] attribute. However, this would require any developer that used this application to have that dll.

    Notice it also says you can use your own attribute! I like that idea, let's do it!

  2. Add Infrastructure Class Library

    Because we may need to use this attribute in several of our libraries and we need to avoid circular dependencies, let's create a new class library. So right click on the Solution in Solution Explorer and choose Add -> Add New Project.

    NDepend Add New Project

    Choose Visual C# -> Windows -> Class Library -> Name it NDependTestInfrastructure.

    NDepend Add Class Library

    Rename Class1 to be NotDeadYetAttribute. Because it's feeling better (monty python joke...). It should prompt you to change the class name in the code. Allow it and also make the class a child of Attribute per the below code block:

    namespace NDependTestInfrastructure
    {
        public class NotDeadYetAttribute: Attribute
        {
        }
    }
    

    Build the application.

  3. Implement Attribute

    NDepend will only recognize libraries that are being used due to the way it uses reflection on the code itself. So before NDepend will recognize the new library, we have to use it.

    Start by adding a reference! Right click on References under NDependTestWebsite and choose Add Reference.

    NDepend Add Reference

    Choose Projects -> Solution and check the box next to NDependTestInfrastructure and hit OK.

    NDepend Add Reference Manager

    If you do not see the option, cancel out, build the solution and then try again.

    In the NDependTestWebsite go to Migrations and open the Configuration.cs file. Change it be the below by adding the attribute:

        [NDependTestInfrastructure.NotDeadYet]
        internal sealed class Configuration : DbMigrationsConfiguration<NDependTestDAL.MyContext>
        {
    

    Build the application.

  4. Add to NDepend

    Whenever, we add a new project we have to also add it to NDepend. NDepend does not assume every project should be included, this gives you greater control over what you may want metrics on.

    To add the new project go to the top menu and select NDepend -> Windows -> Project Properties.

    NDepend Project Properties Open

    You should see NDependTestInfrastructure listed in the right column. Right click on it and select Move selected to application assemblies

    NDepend Move Selected

    This will move it over to the left side. Now in the top bar of this window you'll see a play button with a page behind it. That is the Run Analysis and Build Report button. Click that!

    NDepend Project Properties Analysis

    If you go back to the Queries and Rules Explorer, you'll see it is still reporting 1 Potentially dead Types. Let's fix that by modifying the rule. :)

  5. Modifying the rule attribute

    In the Queries and Rules Explorer window click on Potentially dead Types. Which will open the Queries and Rules Edit window.

    In the LINQ code in the top window you will see where it is flagged to only show methods that do not have the attribute NDepend.Attributes.IsNotDeadCodeAttribute. Change that per below to flag off of our own custom attribute:

             // If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute 
             // and adapt the source code of this rule.
             // USING OUR OWN ATTRIBUTE OF AWESOMENESS!
            !t.HasAttribute("NDependTestInfrastructure.NotDeadYetAttribute".AllowNoMatch()) &&
    

    As soon as you replace the attribute string you will see the Configuraion class disappear! :) Save it! Remember, this changed rule is saved in the ndproj file :)

Changing a rule

  1. Let's look at changing one of the rules a bit more dramatically. Let's go to the Queries and Rules explorer, go to Code Quality and select Methods too complex - critical.

    The interesting part of this match is that it hits the ManageContoller and AccountController which are two controllers we got for free from .Net's default mvc membership. Let's say as a company we decided to dramatically increase what we classified as a complex method. Look at the rule's LINQ code:

    warnif count > 0 from m in JustMyCode.Methods where 
      m.CyclomaticComplexity > 30 ||
      m.ILCyclomaticComplexity > 60 ||
      m.ILNestingDepth > 6
      orderby m.CyclomaticComplexity descending,
              m.ILCyclomaticComplexity descending,
              m.ILNestingDepth descending
    select new { m, m.CyclomaticComplexity, 
                    m.ILCyclomaticComplexity,
                    m.ILNestingDepth  }
    

    If we look closely at the where statement and we compare the report below we can play with those numbers. Let's focus on the m.ILNesting Depth which is currently set to alert us if the depth of a method is more than 6. Change that number to 8 and as soon as you make the change the window below will refresh and only show two violators. Let's go more dramatic and change the nesting depth to only flag if it is greater than 11. Per below:

    warnif count > 0 from m in JustMyCode.Methods where 
      m.CyclomaticComplexity > 30 ||
      m.ILCyclomaticComplexity > 60 ||
      m.ILNestingDepth > 11
      orderby m.CyclomaticComplexity descending,
              m.ILCyclomaticComplexity descending,
              m.ILNestingDepth descending
    select new { m, m.CyclomaticComplexity, 
                    m.ILCyclomaticComplexity,
                    m.ILNestingDepth  }
    

    Obviously this is far more extreme than I would typically do but it does illustrate the point. Let's go farther!

  2. Intermediate Rule Modification

    Let's go back to the Queries and Rules Explorer then to Visibility and let's revist the "Methods that could have a lower visibility" rule. It shows 162 violations in NDependTestWebsite. If you expand that you will see we have 69 methods failing this rule in the NDependTestWebsite.Controllers, dig a little farther and as you open the AccountController you will see 23 methods just in that class are an issue.

    The rule really wants us to lower those public methods to internal or private, however, each of those controller methods is an entry point and as we discovered earlier with the Login method. If we change that, our site will not work! We could go through and add attributes everywhere, but 69 attributes is a lot...

    Instead, let's look at the rule and see how we can change it:

        
    warnif count > 0 from m in JustMyCode.Methods where 
      m.Visibility != m.OptimalVisibility &&
      !m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
      !m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
      // If you don't want to link NDepend.API.dll, you can use your own attributes 
      // and adapt the source code of this rule.
      
      // Eliminate default constructor from the result.
      // Whatever the visibility of the declaring class,
      // default constructors are public and introduce noise
      // in the current rule.
      !( m.IsConstructor && m.IsPublic && m.NbParameters == 0) &&
      // Don't decrease the visibility of Main() methods.
      !m.IsEntryPoint
    select new { 
       m, 
       m.Visibility , 
       CouldBeDeclared = m.OptimalVisibility,
       m.MethodsCallingMe 
    }
    

    You might notice there is a flag for !m.IsEntryPoint, presumably that's looking for Main methods or other methods flagged to be entry points. The 69 methods we are currently looking at are all in our Controllers, which all inherit from the Controller class. So what we really need is to say that it sould not count the method if it is an EntryPoint or in a class that has a parent that is named Controller.

    Thanks to intellisense, let's dig around and see what we get! We can even print out the results to the report below by adding properties to the select :). So inside the select new {} anonymous object block, add a comma after m.MethodsCallingMe and hit m. and see what intellinsense gives you. With a little digging you should end up my code is per below

        // Methods that could have a lower visibility
    warnif count > 0 from m in JustMyCode.Methods where 
      m.Visibility != m.OptimalVisibility &&
      !m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
      !m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
      // If you don't want to link NDepend.API.dll, you can use your own attributes 
      // and adapt the source code of this rule.
      
      // Eliminate default constructor from the result.
      // Whatever the visibility of the declaring class,
      // default constructors are public and introduce noise
      // in the current rule.
      !( m.IsConstructor && m.IsPublic && m.NbParameters == 0) &&
      // Don't decrease the visibility of Main() methods.
      !m.IsEntryPoint
    select new { 
       m, 
       m.Visibility , 
       CouldBeDeclared = m.OptimalVisibility,
       m.MethodsCallingMe,
        m.ParentType.BaseClass.Name //AWESOMENESS
    }
    

    You should now see an additional column below with the column name of Name and the result of Controller.

    NDepend Visibility Before Controllers

    Let's tweak the rule and tell it to also ignore any methods where that property is equal to Controller per the below code:

    // Methods that could have a lower visibility
    warnif count > 0 from m in JustMyCode.Methods where 
      m.Visibility != m.OptimalVisibility &&
      !m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
      !m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
      // If you don't want to link NDepend.API.dll, you can use your own attributes 
      // and adapt the source code of this rule.
      
      // Eliminate default constructor from the result.
      // Whatever the visibility of the declaring class,
      // default constructors are public and introduce noise
      // in the current rule.
      !( m.IsConstructor && m.IsPublic && m.NbParameters == 0) &&
      // Don't decrease the visibility of Main() methods.
      !m.IsEntryPoint
      // Ignore methods in a class that has a parent of Controller
      && m.ParentType.BaseClass.Name != "Controller"
    select new { 
       m, 
       m.Visibility , 
       CouldBeDeclared = m.OptimalVisibility,
       m.MethodsCallingMe,
       m.ParentType.BaseClass.Name //AWESOMENESS
    }
    

    Wow that just took out 61 of our problems!! That's a pretty big chunk. We could start working out even more. But that should give you the general gist of it.

    NDepend Visibility After Controllers

Wrapping it up!

  1. Fixed remaining criticals

    If we go back to the Queries and Rules Explorer you will see we still have 2 critical errors giving us grief. They are both in Dead Code -> Potentially dead Methods. Clicking on that, you should see it is our adorably annoying but amazingly powerful NDependTestWebsite.Migrations.Configuration's ctor and in NDependTestWebsite.Controllers.ManagerController.HasPhoneNumber which must be a method in .Net's default authorization.

    So, we have two things to do, add our NotDeadYet attribute to both.. and change the rule to use OUR version of the not dead attribute.

    Take a stab at making the change! Don't worry... I can wait...

    Done yet?

    How about now?

    If you have rolled your eyes, you may continue... :) Here is how I fixed it.

    In NDependTestWebsite.Migrations.Configuration I changed the ctor to look as such:

            [NDependTestInfrastructure.NotDeadYet]
            public Configuration()
            {
                AutomaticMigrationsEnabled = false;
            }
    

    Then in NDependTestWebsite.Controllers.ManagerController I seriously considering commenting or deleting the HasPhoneNumber method, but in the end, decided to make the change per below:

            [NDependTestInfrastructure.NotDeadYet]
            private bool HasPhoneNumber()
            {
                var user = UserManager.FindById(User.Identity.GetUserId());
                if (user != null)
                {
                    return user.PhoneNumber != null;
                }
                return false;
            }
    

    Finally, in the Queries and Rules Edit Window, I changed the part of the rule that matches the not dead attribute per below:

                 // If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute 
             // and adapt the source code of this rule.
             //Using our own attribute!
             !m.HasAttribute("NDependTestInfrastructure.NotDeadYetAttribute".AllowNoMatch()))
    

    Save! Then hover over the icon in the bottom right and choose Run Analysis and Build Report.

    Yellow! :)

Conclusion

  1. Final Words

    NDepend has some great video tutorials on their site at NDepend Docs that go far more indepth than I could here.

    Hopefully, by taking a short tour of NDepend you have gained some insight on how this tool can impact your process. The effects on larger applications can be astounding, however, even on smaller applications you can see how it will help a lot to weed out potential issues and button up holes. The Methods too complex rule would have been great to work through and reduce the complexity to make it easier for future developers to work with. Or one of my favorites is Methods with too many parameters! With hundreds of pre-built rules you can get up and rocking with this tool in no time. So you can start eliminating your technical debt. Or even quickly delete the rules that don't fit your coding pattern or that of your business.

    I have seriously only scratched the very surface of what NDepend does. It includes some great maps for viewing data from code coverage reports. NDepend allows you to import coverage files from NCover, Visual studio and even JetBrains DotCover.

    If you use TFS for your source control, NDepend also has some code DIFF tools under NDepend -> Options -> Source File Compare Tool that can make life significantly easier.

    I meant to mention SonarQube integration too! Okay, so yeah, blog post has gotten long enough... :)

    Either way, it's great to be blogging again! Thank you for reading and please email me or comment with any questions!

You must be logged in to comment.