NDepend

Improve your .NET code quality with NDepend

10 Visual Studio Navigation Productivity Tips

A large code base is a complex asset. Visual Studio is a complex environment. In this context developers spend a significant part of their time searching and navigating across code elements and the multiple IDE tools and options.

Fortunately Visual Studio proposes many features to simplify navigation that I am going to present.  Note that this post won’t go through the Find in Files Ctrl+Shift+F feature that I guess all developers already use.

Visual Studio: Find in Files
Visual Studio: Find in Files

Short GIF is an excellent way to quickly learn Visual Studio productivity tips. See others related posts based also on short GIFs here:

1) Title Bar Search Box

The Visual Studio title bar proposes a search box. This feature is quite convenient because it let’s search through various kinds of assets: code elements, files, Visual Studio commands, Visual Studio settings… For example in the GIF below:

  • We first search for the interface IAffiliateService
  • Then we type IAS to search again for the interface IAffiliateService: this is Camel Case searching and this is quite convenient to locate large identifier
  • Then we search for Go to: many Visual Studio commands are then matched
  • Finally we search for line number: several Visual Studio settings related to line numbering are then matched.

At first glance being able to search various kinds of assets is a bit weird but once you’ll get used to it this is quite helpful. Personally I use this box to mostly search through commands and settings and use Go to all to search in code.

Visual Studio: Search Box in Title Bar
Visual Studio: Search Box in Title Bar

2) Go to All

When it comes to searching for code elements Go to All (hotkey Ctrl+t) is my preferred Visual Studio feature. Unlike the title bar search box it cannot be used to search for Visual Studio commands or settings.

Go to All offers several filters to define the kind of element to search. These filters can be listed with Ctr+t and then ?:

Visual Studio: Go to All Filters

In the GIF below:

  • We first search for all code elements that start with IAffiliateService: both source files and interfaces are matched
  • Then we prefix the same research with the filter “t ” to narrow the search on types only.
  • Then again we use the Camel Case searching on type with “t IAS”
  • Finally we list filters by typing “?”
Visual Studio Go to All Ctrl+t
Visual Studio Go to All Ctrl+t

There are also two scopes tickbox to:

  • Search only in Current Document (Ctrl+Alt+C)
  • Include contents of external items (Alt+X)

There are also 3 settings:

Visual Studio: Go to All Settings
Visual Studio: Go to All Settings

3) Peek Definition and Go To Definition

When the code editor carret is over an identifier you can use the hotkeys:

  • Alt+F12 to peek the identifier definition
  • F12 to open the source file that contains the definition

I believe those two hotkeys must be mastered by all Visual Studio developers.

Visual Studio: Peek Definition (Alt+F12) Go To Definition (F12)
Visual Studio: Peek Definition (Alt+F12) Go To Definition (F12)

Now let’s present other navigation hotkeys available when the carret is over an identifier in the source code: they are all available through the right-click menu.

Visual Studio: Navigation Hotkeys
Visual Studio: Navigation Hotkeys

4) Find All References

When the code editor carret is over an identifier you can use the hotkey Shift+F12 to find all references:

Visual Studio: Find all References
Visual Studio: Find all References

Typically the Find all references results window is a vertical windows so I like to get rid of all columns except the code one to obtain a result easier to browse.

Visual Studio: Clear columns of Find all references
Visual Studio: Clear columns of Find all references

A Group-By combo box let’s choose among various options:

Visual Studio: Find all references Group-By combo box
Visual Studio: Find all references Group-By combo box

I hope that in the future (post Visual Studio 2019) this result list will be improved this ways:

  • I’d like to group by Project, Namespace, Type, Member but this grouping option is not available.
  • On the screenshot above we see an unnecessary [Containing Member: Unknow] row that consumes space without adding any value.
  • The parent rows are in bold, I am not sure why, this makes the source code row less readable.
  • The same way the reference counting (3) (1) shouldn’t be in bold.
  • The parent rows should be prefixed with the corresponding icon (project icon, class icon, method icon…).

5) CodeLens

Visual Studio Code Lens is an intuitive an immediate way to see all references of an identifier. It is only available at the identifier declaration, not at the identifier usage locations.

Visual Studio: Find References with Code Lens
Visual Studio: Find References with Code Lens

I remember when Code Lens was introduced a few years ago I didn’t like the extra vertical space between lines and that some extra info were added in the middle of source code. So I used to disable it. However with time I got used to it and learnt to like it because it also displays more information than just references (code changes, branch… see the details here).

6) Go to Base and Go to Implementation

When the code editor carret is over an identifier representing a class name or an interface name:

  • The shortcut Alt+Home (Go to Base) can be used to go to the base class(es) or implemented interfaces.
  • The shortcut Ctrl+F12 (Go to Implementation) can be used to go to derived classes or classes that implement the interface.
Visual Studio: Go to Base, Go to Implementation
Visual Studio: Go to Base, Go to Implementation

7) Call Hierarchy

When the code editor carret is over an identifier representing a member, the shortcut Ctrl+K Ctrl+T opens the Call Hierarchy Window. With this window you can browse callers and also callers of callers (recursive).

Visual Studio: Call Hierarchy
Visual Studio: Call Hierarchy

When it comes to call hierarchy I prefer to have a dependency graph view. The NDepend dependency graph is well suited for that. The GIF below illustrates this list of action:

  • Right click the definition of a method, a field, a type or a namespace.
  • Then click Show on Dependency Graph
  • Then click callers in the graph navigation bar
  • Then, if needed, adjust Group-By and Layout Horizontal / Vertical.
NDepend Call Graph
NDepend Call Graph

8) Navigate TODO comments in Task List

The Visual Studio Task List Window can display all comment lines starting with the predefined token TODO (case sensitive).

Visual Studio: TODO Task List
Visual Studio: TODO Task List

You can define your own set of tokens in Options > Environment > Task List:

Visual Studio Task List Settings
Visual Studio Task List Settings

9) Shortcut on a line of code

A shortcut can be placed to any line of code with the hotkey Ctrl+K-H. The same hotkey can be used to remove the shortcut.

These shortcuts are listed in the Task List Window. Notice that shortcuts are persisted and if you close and restart Visual Studio they will still be there.

Visual Studio: Shortcut on line of code
Visual Studio: Shortcut on line of code

Notice that this feature is different that the Bookmark feature already presented in 10 Visual Studio Ninja Code Editor Productivity Tips (item 10).

10) Go to Matching Brace

When the carret is over an opening brace or a closing brace { }, the hotkey Ctrl+} goes to the matching brace. This also work for these pairs: ( )  [ ] <>

Visual Studio: Go to Matching Brace
Visual Studio: Go to Matching Brace

Conclusion

I hope that you’ve learnt some tricks to improve your productivity.

It is important to train a bit and get used to all these features and which one to favor for which scenario.

  • Title Bar Search box is well suited to search across Visual Studio menus and options
  • Ctrl+t Go to All is well suited to search files and code element identifiers.
  • Then it is important to master all navigation features around the F12 key: F12 Go to Definition, Alt+F12 Peek Definition, Shift+F12 Find all References, Alt+Home Go to Base, Ctrl+F12 Go to Implementation

I mentioned the NDepend dependency graph in the Call Hierarchy tips. This tool can help a lot when it comes to navigating and understanding a large code base. Here is a short introduction video:

 

Top 10 Visual Studio Refactoring Tips

With the version 2019 Visual Studio is now mature when it comes to refactoring. This post proposes a tour of the top 10 most used refactoring actions in my opinion.

Short GIF is an excellent way to help get started with those Visual Studio tips. See others related posts based also on short GIFs here:

1) Renaming an Identifier

With Ctrl+R,R you can rename any code identifier: a variable, a field, a class… The renaming experience is pretty clean when only one source file is concerned since all references in file get updated live while typing the new name:

Renaming a variable in Visual Studio
Renaming a variable in Visual Studio

When renaming an identifier impacts several source files, like when renaming a class, several UI details improve the user experience:

  • In the top-right panel we see: Renaming X references in Y files
  • In the top-right panel we get the checkbox: Rename file, this is quite useful since often the file name and the class name are in-sync.
  • We get the possibility to preview all changes in all files in a Preview Change window.
Renaming a class in Visual Studio
Renaming a class in Visual Studio

2) Extract Method

The hotkey Ctrl+. triggers the Quick Actions and Refactorings menu. If you are not used yet to Ctrl+. I’d advise training especially this one because it is a powerful shortcut. You’ll see that most other refactoring presented here rely on the Ctrl+. hotkey.

If one or several instructions are actually selected in a method, the Extract method and Extract local function menus are proposed. A large tooltip is immediately shown to preview the changes. Then just click Enter and terminate the refactoring action by naming the NewMethod identifier.

Extract Method with Visual Studio
Extract Method with Visual Studio

3) Remove and Sort Usings

To make your code cleaner it is recommended to maintain for each source file the list of using ordered alphabetically with unnecessary usings removed. Both actions can be done automatically with Visual Studio top menu > Edit > Intellisense > Remove and Sort Usings. I wish a default shortcut was assigned to this common refactoring (of course you can still assign a shortcut to this action from Visual Studio top menu > Tools > Options > Keyboards.).

Remove and Sort Usings with Visual Studio
Remove and Sort Usings with Visual Studio

Actually it is possible to remove all unnecessary usings at once in a Visual Studio project or solution thanks to the bulb that appears in the code editor gutter when selecting an unnecessary using faded away.

Remove unnecessary usings with Visual Studio
Remove unnecessary usings with Visual Studio

4) Add Missing Usings when Pasting

When pasting some code it is quite irritating to get some errors because of some missing usings. Once the code has been pasted, you can click Ctrl+. to ask Visual Studio to add missing usings for you:

Add missing usings after pasting with Visual Studio
Add missing usings after pasting with Visual Studio

5) Generate Property from Constructor and Generate Constructor from Properties

Once again the magical Ctrl+. hotkey can be used when selecting a parameter into a constructor signature, to generate the corresponding property.

However I haven’t found a way to generate several properties in a row. I’d expect that selecting several parameters and then Ctrl+. would propose to generate several properties in a row but it is not the case, and there is no Create properties for all parameters menu.

Generate property from constructor with Visual Studio
Generate property from constructor with Visual Studio

The same way you can generate a constructor from the selected properties.

Generate constructor from selected properties with Visual Studio
Generate constructor from selected properties with Visual Studio

Both quick actions work with fields also.

6) foreach to LINQ

When the editor carret is over a foreach loop, the hotkey Ctrl+. proposes to convert the foreach loop to a LINQ query. This conversion is not always possible but it is smart enough. For example it can convert a if(condition){continue} within the loop into a where condition LINQ clause.

Notice that typically loops are faster than LINQ queries because the compiler can optimize loops while LINQ queries extensively rely on method calls. But in non-performance-critical code region LINQ queries is often a more readable, concise and maintainable way of writing code.

Converting a foreach loop into a LINQ query with Visual Studio
Converting a foreach loop into a LINQ query with Visual Studio

7) Extract Interface

When the editor carret is over a class name the hotkey Ctrl+. proposes a quick-actions list that includes: extract an interface from the class members. Ctrl+R,I can be used instead to directly show the Extract Interface dialog. The Extract Interface dialog proposes to define the interface name (per defaut set to “I{class name}”) and the member list to include in the interface.

Extracting an interface from a class with Visual Studio
Extracting an interface from a class with Visual Studio

8) Move Type to Namespace

When the editor carret is over a type name, the hotkey Ctrl+. proposes the quick-actions list that includes: move to namespace. The Move to namespace dialog is smart enough to propose intellisense and auto-completion based on existing namespaces. However the type’s source file is not moved automatically to the folder corresponding to the namespace chosen, this must be done manually in the Solution Explorer. I hope this move will be done automatically in the future.

Move class to namespace with Visual Studio
Move class to namespace with Visual Studio

9) Add Parameter to a Method

You can add a named parameter to a method call location. Then the hotkey Ctrl+. proposes the refactoring Add parameters to the method called. The method signature is then refactored with the extra parameter but other calls of the method are left untouched. It means that now these other calls provoke a syntax error and must be fixed with the extra parameter.

Add parameter to a method with Visual Studio
Add parameter to a method with Visual Studio

10) Convert to interpolated string and simplify interpolation

When the carret is over a call to string.Format() call the hotkey Ctrl+. proposes the refactoring Convert to interpolated string. String interpolation with the syntax $”{parameter}” has been introduced with C#6 in 2015. Then if possible some elements like call to PadLeft() are grayed in code. This is a sign that the string interpolation can be simplified once again with the hotkey  Ctrl+. over those grayed elements.

Convert to interpolated string with Visual Studio
Convert to interpolated string with Visual Studio

Conclusion

Over the years thanks to massive effort put in Roslyn, Visual Studio got better and better when it comes to refactoring actions proposed out-of-the-box. Many more refactoring than those 10 are proposed: read the list of refactoring and list of quick-actions.

When we talk about Visual Studio and refactoring the case of Resharper immediately comes in the discussion. For more than a decade Resharper has been the tool of choice to improve the productivity with many refactoring actions and more great features. My independent opinion in the VS vs. R# debate (in 2020) is that R# is still a bit more powerful despite VS being now quite mature. However it seems to me that the fact that VS is now quite mature with refactoring is not popular enough, hence I hope this post can help spread the word. And if you ask, yes I still code with R# in VS despite R# slowing down a bit the IDE, but I find myself using it less often. Within the next years we can expect both VS improvements in terms of refactoring and R# improvements especially in terms of performance.


Actually the word refactoring has really two meanings:

  • Short and quick productivity refactoring actions as presented here.
  • Large scale refactoring that are necessary when the architecture of a legacy doesn’t fit anymore the planned evolution and maintainability requirements.

Large scale refactoring must be discussed extensively. The number one prerequisite for a successful large scale refactoring is a solid understanding of the legacy code architecture. This is where the tool NDepend with its new dependency graph and dependency matrix can really help. Here are two short videos that explain how:

Case Study : Complex UI Testing

In the previous post Case Study: 2 Simple Principles to achieve High Code Maintainability I explained that the principles layered code + high coverage ratio by test are 2 simple principles that can be objectively applied, validated and measured. When these 2 principles are applied they lead to High Code Maintainability: As a consequence the management saves money in the long term and developers are happy to work in a cleaner code base with principles easy to follow.

Large and complex UI 90%+ covered by tests

In this previous post I used the example of the new NDepend v2020.1 graph. This new tool is a large and complex UI with dozens of actions proposed to the user and drastic performance requirement (it scales live on 100.000+ elements). This graph implementation is 90% covered by tests. It is not because there is a lot of UI code that it should not been well tested. We didn’t spend a good part of our resources in writing tests just for the sake of it. We did it because we know by experience that it’ll pay off: probably a few bugs will be reported as for all 1.0 implementation although beta test phases already caught some. But we are confident that it won’t take a lot of resources to fix them. We can look forward the future confidently (like supporting properly .NET 5 that will be released in November 2020).  And indeed 10 days after its 1.0 release no bug has been reported (nor logged) on this new graph although many users downloaded it: so far it looks rock-solid and we can focus on what’s next.

The picture below shows all namespace, classes and methods of the graph implementation. Smaller rectangles are methods and the color of each rectangle indicates how well a method is covered by tests. Clearly we tolerate some gaps in UI code, while non UI code like Undo/Redo actions implementations are 100% covered. Experience told us how to balance our resources and that everything does not have to be perfect to achieve high maintainability.

NDepend Graph Implementation 90% covered by tests

How did we achieve High Coverage Ratio on UI Code?

It is easy: we have a simple MVC (Model View Controller) design. Some controller classes contain the logic for all actions the user can do and those classes pilot the UI. Concretely in our scenario actions are: load/save, change group-by, change layout direction, zoom, generate a call graph for this method, change filters…

Then we wrote a test suite that first starts the UI and then invokes all actions. Each complex peculiarity of each action gets fully tested, hence complex actions get invoked several times by tests but differently each time, to make sure all scenarios get tested.

The video below shows the UI under testing: more than 40 actions get tested in less than a minute. It would take more than an hour to do all this work manually and any change in code could potentially ruin the validity of manual tests.

In such a complex UI there are many classes that are not directly related to UI. For example the grape of classes that describe the underlying model are tested separately.

As usual, a side benefit of writing tests is better design : the code gets structured in a way that makes it easy to invoke it through tests. Concretely some abstractions are introduced (that wouldn’t make sense without tests), some classes and some methods get splitted, some logic gets refined and as a result developers are happy to live in a code base where the logic is smoothly implemented.

High Coverage Ratio is not Enough: Assertions to the rescue

Typically at this point comes the remark: but code coverage is not enough, results must be asserted. And indeed, if nothing gets asserted nothing gets tested even if the code is entirely covered by tests.  We want tests to fail if something can go wrong.

Of course our tests contain many assertions for example load / save actions are invoked and asserted this way:

But these assertions are not enough. Per definition the UI code contains tons of visual peculiarities represented by states that can be potentially corrupted. As a consequence our UI code is stuffed with thousands of assertions: everything that can be asserted gets asserted.

  • A Rectangle with width/height in certain range
  • The state of a node or an edge when another element gets selected (is it a caller, a callee…?).
  • The current application state when a new graph is demanded by the controller.
  • The graph UI contains many asynchronous computation to avoid UI freezing. This leads to many assertions to check that mutable states are not corrupted by concurrent accesses.

All those states asserted would be hardly reachable from test code. However they get naturally accessed by the UI code itself so it is the right place to assert that they are not corrupted.

Btw, We still use the good old System.Diagnostics.Debug.Assert(…) for that, it has several advantages:

  • It is simple.
  • It is understood by tools like Roslyn/Resharper/CodeRush analyzers.

  • An assertion that fails cannot be missed both when running automatic tests and when running manual tests on the Debug mode version.
  • Debug assertions are removed by the compiler in Release mode: assertions are not executed in production and users get better performance. The idea is to not consider users as testers: code released in production is supposed to be rock-solid. Assertions are like scaffolding that gets removed when a building gets delivered. If there is still a bug we’ll discover it from users feedback, from production logs or from our own manual tests.

Debug.Assert(…) is enough for us and it is understandable that some other teams wants more sophisticated assertions framework. The key is to take the habit to assert everything that can be asserted when writing code (UI code or not). Each assertion is a guard that helps making the code rock-solid. Also each assertion improves the code readability. At code-review time we’ve all been wondering: can this integer be zero? can this string be empty? can this reference be null?. Hopefully C#8 non-nullable discards the last question but so many questions remain open without assertions.

Design by Contracts

This idea of stuffing code with assertions is actually an important software correctness methodology named DbC, Design by Contract, that is really worth knowing. Contracts mean much more than the usual approach with exception:

  • Explicitly throwing an exception says: zero is tolerated, it is not a bug, but you won’t get the result you’d like, be prepared to catch some exceptions.
  • Writing a contract says: don’t even thing of passing a zero value. The real type of argument is not Int32 it is [Int32 minus 0].  Ideally such violation could be caught by compilers and analyzers (and is indeed sometime caught as we saw in the screenshot above).

Conclusion

Any complex UI can be automatically tested as long as:

  • It is well designed with some controllers that pilot the UI and that can be invoked from tests.
  • UI code gets stuffed with assertions to make sure that no state becomes corrupted at runtime.

In short assertions embedded in code tested matter as much as assertions embedded in tests. If an assertion gets violated there is a problem, no matter the assertion location, and it must not be missed nor ignored. This powerful idea doesn’t necessarily applies only to UI code and is known as DbC, Design by Contract.

Actually in this post I added a third principle to achieve high code maintainablity and high code correctness : layered code + high coverage ratio by test + contracts

Business Complexity vs. Implementation Complexity

It is good software design practice to make sure that methods can be entirely viewed in the code editor that typically shows 30 to 45 lines at a time. The root of this principle is easy to grasp: scrolling up and down over a too large method impedes code readability.

Refactoring a too large method or a too large class implies to create several code elements smaller in terms of number of textual lines. But ultimately the code behavior didn’t change. In other words the business complexity remained the same but the refactor session reduced the implementation complexity (at least we hope so).

Software complexity is a subjective measure relative to the human cognition capabilities. Something is complex when it requires effort to be understood by a human. Software complexity is a 2 dimensional measure. To understand a piece of code one must understand both:

  • What this piece of code is doing at runtime, the behavior of the code, this is the business complexity.
  • How the actual implementation solves the business needs at runtime, this is the implementation complexity.

The whole point of software design, SOLID principles, code maintainability, patterns… is to avoid adding implementation complexity to the business complexity. But from where implementation complexity comes from? There are 5 main sources of implementation complexity:

Too Large Code

We already mentioned this one. It is easy to limit implementation complexity by abiding by simple code rules that check thresholds on code metrics like methods number of lines of code and method complexity, or classes with too many methods. The Single Responsibility Principle (SRP) also contribute to smaller implementations: less responsibilities for a class necessarily means less code.

Lack of Abstractions

An abstraction such as an abstract method, an interface or even a delegate, is the minimum required knowledged to invoke an implementation at runtime without knowing it at design time. In other words an abstraction hides implementation detail. Abstraction represents a great weapon to reduce the implementation complexity because polymorphism can replace quantity of if/else/switch/case. Concretely code like that:

Can be replaced with code like that:

Abstraction also reduces implementation complexity because it frees the developer mind of implementation details.

The S in SOLID is the SRP (mentioned above) and is not related to abstraction. But the OLID principles are all about using abstractions wisely:

How do we check for good usage of abstractions? There is no magic stick like thresholds to limit too large code elements. However the Abstractness vs. Instability graph and metrics and the Level metric are a good start to identify code areas that need more abstractions. They are both described in this post about Dependency Inversion Principle.

State Mutability at Runtime

A common source of implementation complexity is mutable states. The human brain is not properly wired to anticipate what is really happening at runtime in a program. While reviewing a class, it is hard to imagine how many instances will simultaneously exists at runtime and how the states of each these instances will evolve over time. This is actually THE major source of problems when dealing with a multi-threaded program.

If a class is immutable (if the states of all its instances objects don’t change at runtime once the constructor is called) its runtime behavior immediately becomes much easier to grasp and as a bonus, one doesn’t need to synchronize access to immutable objects. See here a post explaining in-depth immutable class.

A method is a pure function if it produces outputs from its inputs without modifying any state. Like immutable classes, pure functions are also a good mean to reduce implementation complexity.

Some code rules exists to enforce state mutability like Structure should be Immutable or Fields should be marked as ReadOnly when possible. Being immutable for a class or pure for a method is such an essential property that dedicated C# keywords for that would be welcome, like readonly for fields. Here is a proposal for C# support of the immutable keyword. By now some ImmutableAttribute or PureAttribute can be used to tag such elements and some code rule can check for Types tagged with ImmutableAttribute must be immutable or Methods tagged with PureAttribute must be pure

Over Coupling

When trying to re-engineer/understand/refactor a large piece of code (I mean something made of dozens of classes like a big namespaces or an assembly), the difficulty is directly proportional to the amount of coupling between considered code elements. Both dependency graphs below shows dependencies between 36 classes: but the left contains 64 edges and the right one contains 133 edges: which one would you prefer to deal with?

One key strategy to control coupling is to layer components and make sure that there is no dependency cycles. This can be checked on namespaces for example with the code rule Avoid namespaces dependency cycles. Using abstractions is also a good way to limit over coupling. If an interface has N implementations then relying only on one interface is virtually like depending on the N underlying classes.

Lack of Unit Tests

Software testing is a large topic I won’t cover here. But one key benefit of writing tests (apart enforcing business rules and detecting regressions early) is to ensure that the code is testable. Being testable for code means less coupling, more abstractions and overall simple code. Ultimately if the code is easily testable we can safely assume that its implementation complexity is kept low. Here also many code rules like Code should be tested can help enforce high testability.

One Measure for All

There are more sources of implementation complexity but those 5 ones are certainly the bigger culprits. To reduce this unnecessary complexity one must be able to measure it. But how to unify too large code, bad design, poorly tested code and more in a single metric?

As we saw most of these aspects can be enforced with code rules. A code rule produces issues and for each issue the code rule can estimate the cost to fix an issue and the annual cost to let an issue unfixed. A famous analogy with the financial field says that:

  • The estimated cost to fix code smells is the Technical-Debt: a measure of the implementation complexity.
  • The estimated annual cost to let code smells unfixed is the Annual Interest of the Debt: a measure of the business operation cost induced by poorly written and poorly tested code.

These estimations can be expressed in developer-time and ultimately in money cost which can be used by management to take the right decisions.

Identify .NET Code Structure Patterns with no Effort

The two pillars of code maintainability are automatic testing and clean code structure.

  • Testing is used to regularly challenge code correctness and detect regression early. Testing can be easily assessed with numbers like code coverage ratio and the amount of assertions tested.
  • A clean code structure prevents the phenomenon of spaghetti code, entangled code that is hard to understand and hard to maintain. However assessing the code structure cannot be achieved through numbers like for testing. Moreover the structure emerges from a myriad of details buried in many source files and thus appropriate tooling is needed.

For most engineers, code dependency graph is the tool of choice to explore code structure. Boxes and arrows graph is intuitive and well adapted to visualize a small amount of dependencies. However to visualize complex portion of code the Dependency Structure Matrix (DSM) is more adapted. See below the same set of 34 namespaces visualized with the NDepend Dependency Graph and the NDepend Dependency Matrix.

NDepend dependency graph
NDepend dependency graph

 

NDepend Dependency Matrix
NDepend Dependency Matrix

If the concept of dependency matrix is something new to you, it is important to note that:

  • The Matrix headers’ elements represent graph boxes
  • The Matrix non-empty cells correspond to graph arrows. Numbers on the cells represents a measure of the coupling in terms of numbers of methods and fields involved. In a symmetric matrix a pair of blue and green cell is symmetric because both cells represents the same thing: the blue cell represents A uses B and the green cells represents B is used by A.

Here is a 5 minutes introduction video if you are not familiar with the dependency matrix:

Clearly the graph is more intuitive, but apart the two red arrows that represent two pairs of namespaces mutually dependent this graph tells few things about the overall structure.

On the other hand the matrix algorithm naturally attempts to layer code elements, exhibit dependency cycles, shows which element is used a lot or not… Let’s enumerate some structural patterns that can be visualized at a glance with the dependency matrix:

Layers

One pattern that is made obvious by a DSM is layered structure (i.e acyclic structure). When the matrix is triangular, with all blue cells in the lower-left triangle and all green cells in the upper-right triangle, then it shows that the structure is perfectly layered. In other words, the structure doesn’t contain any dependency cycle.

On the right part of the snapshot, the same layered structure is represented with a graph. All arrows have the same left to right direction. The problem with graph, is that the graph layout doesn’t scale. Here, we can barely see the big picture of the structure. If the number of boxes would be multiplied by 2, the graph would be completely unreadable. On the other side, the DSM representation wouldn’t be affected; we say that the DSM scales better than graph.

Notice that NDepend proposes 2 rules out of the box to control layering by preventing dependency cycles to appear: ND1400 Avoid namespaces mutually dependent and ND1401 Avoid namespaces dependency cycles.

Interestingly enough, most of graph layout algorithms rely on the fact that a graph is acyclic. To compute layout of a graph with cycles, these algorithms temporarily discard some dependencies to deal with a layered graph, and then append the discarded dependencies at the last step of the computation.

Cycles

If a structure contains a cycle, the cycle is displayed by a red square on the DSM. We can see that inside the red square, green and blue cells are mixed across the diagonal. There are also some black cells that represent mutual direct usage (i.e A is using B and B is using A).

The NDepend’s DSM comes with the option Indirect Dependency. An indirect dependency between A and B means that A is using something, that is using something, that is using something … that is using B. Below is shown the same DSM with a cycle but in indirect mode. We can see that the red square is filled up with only black cells. It just means that given any element A and B in the cycle, A and B are indirectly and mutually dependent.

Here is the same structure represented with a graph. The red arrow shows that several elements are mutually dependent. But the graph is not of any help to highlight all elements involved in the parent cycle.

Notice that in NDepend, we provided a button to highlight cycles in the DSM (if any). If the structure is layered, then this button has for effect to triangularize the matrix and to keep non-empty cells as closed as possible to the diagonal.

High Cohesion / Low-Coupling

The idea of high-cohesion (inside a component) / low-coupling (between components) is popular nowadays. But if one cannot measure and visualize dependencies, it is hard to get a concrete evaluation of cohesion and coupling. DSM is good at showing high cohesion. In the DSM below, an obvious squared aggregate around the diagonal is displayed. It means that elements involved in the square have a high cohesion: they are strongly dependent on each other although. Moreover, we can see that they are layered since there is no cycle. They are certainly candidate to be grouped into a parent artifact (such as a namespace or an assembly).

On the other hand, the fact that most cells around the square are empty advocate for low-coupling between elements of the square and other elements.

In the DSM below, we can see 2 components with high cohesion (upper and lower square) and a pretty low coupling between them.

While refactoring, having such an indicator can be pretty useful to know if there are opportunities to split coarse components into several more fine-grained components.

Too many responsibilities

The popular Single Responsibility Principle (SRP) states that: a class shouldn’t have more than one reason to change. Another way to interpret the SRP is that a class shouldn’t use too many different other types. If we extend the idea at other level (assemblies, namespaces and method), certainly, if a code element is using dozens of other different code elements (at same level), it has too many responsibilities. Often the term God class or God component is used to qualify such piece of code.

DSM can help pinpoint code elements with too many responsibilities. Such code element is represented by columns with many blue cells and by rows with many green cells. The DSM below exposes this phenomenon.

Popular Code Elements

A popular code element is used by many other code elements. Popular code elements are unavoidable (think of the String class for example).

A popular code element is not a flaw. However it is advised that popular elements are interfaces and enumerations. This way consumers rely on abstractions and not on implementations details. The benefit is that consumers are less often broken because abstraction are less subject to change than implementations.

A popular code element is represented by columns with many green cells and by rows with many blue cells. The DSM below highlights a popular code element.

Something to notice is that when one is keeping its code structure perfectly layered, popular components are naturally kept at low-level. Indeed, a popular component cannot de-facto use many things, because popular component are low-level, they cannot use something at a higher level. This would create a dependency from low-level to high-level and this would break the acyclic property of the structure.

Mutual dependencies

You can see the coupling between 2 components by right clicking a non-empty cell, and select the menu Open this dependency.

If the opened cell was black as in the snapshot above (i.e if A and B are mutually dependent) then the resulting rectangular matrix will contains both green and blue cells (and eventually black cells as well) as in the snapshot below.

In this situation, you’ll often notice a deficit of green or blue cells (3 blue cells for 1 green cell here). It is because even if 2 code elements are mutually dependent, there often exists a natural level order between them. For example, consider the System.Threading namespaces and the System.String class. They are mutually dependent; they both rely on each other. But the matrix shows that Threading is much more dependent on String than the opposite (there are much more blue cells than green cells). This confirms the intuition that Threading is upper level than String.

Static analysis of .NET Core 2.0 applications

NDepend v2017.3 has just been released with major improvements. One of the most requested features, now available, is the support for analyzing .NET Core 2.0 and .NET Standard 2.0 projects. .NET Core and its main flavor, ASP.NET Core, represents a major evolution for the .NET platform. Let’s have a look at how NDepend is analyzing .NET Core code.

Resolving .NET Core third party assemblies

In this post I’ll analyze the OSS application ASP.NET Core / EntityFramework MusicStore hosted on github. From the Visual Studio solution file, NDepend is resolving the application assembly MusicStore.dll and also two test assemblies that we won’t analyze here. In the screenshot below, we can see that:

  • NDepend recognizes the .NET profile, .NET Core 2.0, for this application.
  • It resolves several folders on the machine that are related to .NET Core, especially NuGet package folders.
  • It resolves all 77 third-party assemblies referenced by MusicStore.dll. This is important since many code rules and other NDepend features take into account what the application code is using.

It is worth noticing that the .NET Core platform assemblies have high granularity. A simple website like MusicStore references no fewer than 77 assemblies. This is because the .NET Core framework is implemented through a few NuGet packages that each contain many assemblies. The idea is to release the application only with needed assemblies, in order to reduce the memory footprint.

.NET Core 2.0 third party assemblies granularity

NDepend v2017.3 has a new heuristic to resolve .NET Core assemblies. This heuristic is based on .deps.json files that contain the names of the NuGet packages referenced. Here we can see that 3 NuGet packages are referenced by MusicStore. From these package names, the heuristic will resolve third-party assemblies (in the NuGet store) referenced by the application assemblies (MusicStore.dll in our case).

NuGet packages referenced in .deps.json file

Analyzing .NET Standard assemblies

Let’s be clear that NDepend v2017.3 can also analyze .NET Standard assemblies. Interestingly enough, since .NET Standard 2.0, .NET Standard assemblies reference a unique assembly named netstandard.dll and found in C:\Users\[user]\.nuget\packages\NETStandard.Library\2.0.0\build\netstandard2.0\ref\netstandard.dll.

By decompiling this assembly, we can see that it doesn’t contain any implementation, but it does contain all types that are part of .NET Standard 2.0. This makes sense if we remember that .NET Standard is not an implementation, but is a set of APIs implemented by various .NET profiles, including .NET Core 2.0, the .NET Framework v4.6.1, Mono 5.4 and more.

Browsing how the application is using .NET Core

Let’s come back to the MusicStore application that references 77 assemblies. This assembly granularity makes it impractical to browse dependencies with the dependency graph, since this generates dozens of items. We can see that NDepend suggests viewing this graph as a dependency matrix instead.

NDepend Dependency Graph on an ASP.NET Core 2.0 project

The NDepend dependency matrix can scale seamlessly on a large number of items. The numbers in the cells also provide a good hint about the represented coupling. For example, here we can see that  22 members of the assembly Microsoft.EntityFrameworkCore.dll are used by 32 methods of the assembly MusicStore.dll, and a menu lets us dig into this coupling.

NDepend Dependency Matrix on an ASP.NET Core 2.0 project

Clicking the menu item Open this dependency shows a new dependency matrix where only members involved are kept (the 32 elements in column are using the 22 elements in rows). This way you can easily dig into which part of the application is using what.

NDepend Dependency Matrix on an ASP.NET Core 2.0 project

All NDepend features now work when analyzing .NET Core

We saw how to browse the structure of a .NET Core application, but let’s underline that all NDepend features now work when analyzing .NET Core applications. On the Dashboard we can see code quality metrics related to Quality Gates, Code Rules, Issues and Technical Debt.

NDepend Dashboard on an ASP.NET Core 2.0 project

Also, most of the default code rules have been improved to avoid reporting false positives on .NET Core projects.

NDepend code rules on an ASP.NET Core 2.0 project

We hope you’ll enjoy using all your favorite NDepend features on your .NET Core projects!