Improve your .NET code quality with NDepend

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).


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

Exploring .NET Core 3.0 new API

.NET Core 3.0 is representing a major step for the .NET community. It is interesting to analyze what’s new in the API directly from the compiled bits. In this post I will first explain how to diff .NET Core 3.0 against .NET Core 2.2 with NDepend, and then how to browse diff results.

Arguably the biggest progress of .NET Core 3.0 will be the support for Winforms and WPF on the Windows platform. Since everything is new here, compare to .NET Core 2.2, we won’t analyze this part. However it will be interesting to analyze .NET Fx Winforms/WPF APIs vs .NET Core 3.0 Winforms/WPF APIs in another post (that I finally wrote here).

Analyzing two versions of .NET Core with NDepend

It takes a few minutes to download NDepend trial, install it and start VisualNDepend.exe, and it takes a few minutes to compare .NET Core 3.0 against .NET Core 2.2. If you want to browse the diff on your machine, expect 5 to 10 minutes to get hands-on.

First Start VisualNDepend.exe and click Compare 2 versions of a code base:

How to compare 2 versions of a code base

For both builds, choose Add Assemblies in Folder:

  • Choose C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.2  for Older Build
  • Choose C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0-preview-27324-5 for Newer Build
Choose assemblies in folder to analyze

Respectively 156 and 161 assemblies are gathered. Click Ok to run two analysis, on older and newer build. Both analysis results will be then diffed automatically.

Ready to compare 2 versions of .NET Core

Querying new API

Let’s start with a few CQLinq code queries to explore the new .NET Core 3.0 APIs:

This query match all new public code elements, including new assemblies, namespace, types, methods and fields:

Use the NDepend query result to browse this large new API set : 5 new assemblies, 83 new namespaces, 297 new types, 4 924 new methods and 307 new fields. Note that code elements with pink background are not matched by the query, they are just here for preserving the code hierarchy in the result:

.NET Core 3.0 new public API

Download here this long list obtained by exporting the query result to excel. For a better result formatting I actually used this refined query to show properly parent assemblies/namespaces/types in excel columns:

It is interesting to just focus on the 297 new public types with the code query below. Download the list here or browse the same list at the end of this post.

New .NET Core 3.0 Public Types

It is also interesting to browse the new 1.101 public methods and 38 public fields added on public types that existed already in .NET Core 2.2.  Download this list here.

API Breaking Changes

NDepend proposes 6 default rules to browse API breaking changes.

These rules matche 19 public types removed from .NET Core 2.2 (see list below) 176 public methods removed and 36 public fields removed

.NET Core 2.2 Types Removed

Listing Methods Changed

Exploring the API evolution is useful for API consumers. For those working on the framework .NET Core itself, it is interesting to also browse implementation changes. The NDepend search by change panel proposes various options for that. Note that this search panel is actually a code query generator. The Edit query button proposes to edit and refine the currently generated query.

Another interesting point is that it is a semantic implementation change. All matched methods do behave differently at runtime. This makes this tool ideal to plan code change review without bothering with formatting and comments change.

Matched code elements can be highlighted in the metric view. From the screenshot above we can see at a glance that System.Xml and System.Data are much more stable than System.RunTime for example. By zooming in the view, we can get more information about which code was churned.

Highlight methods where code was changed

In the query result panel, a code element is underlined when its implementation changed. If you have compiled both source versions on your machine and analyzed those compiled versions, you can right click an underlined method and directly compare the diff in source code.

I hope you see value both in the results offered and in the how-to-diff procedure that can be applied to any .NET code base, assuming you have 2 versions to compare.

New .NET Core 3.0 types

Here is the list of the 297 new types added to .NET Core 3.0.

ParentAssembly Full Name
WindowsBase System.Windows.Markup .ValueSerializerAttribute
System.Xml.Linq System.Xml.XPath.XDocumentExtensions
System.Threading.ThreadPool System.Threading.IThreadPoolWorkItem
System.Threading.Tasks System.Runtime.CompilerServices .AsyncIteratorMethodBuilder
System.Threading.Tasks System.Runtime.CompilerServices .ConfiguredCancelableAsyncEnumerable<T>
System.Text.Json System.Text.Json.JsonCommentHandling
System.Text.Json System.Text.Json.JsonTokenType
System.Text.Json System.Text.Json.JsonDocument
System.Text.Json System.Text.Json.JsonElement
System.Text.Json System.Text.Json .JsonElement+ArrayEnumerator
System.Text.Json System.Text.Json .JsonElement+ObjectEnumerator
System.Text.Json System.Text.Json.JsonProperty
System.Text.Json System.Text.Json.JsonValueType
System.Text.Json System.Text.Json.JsonReaderException
System.Text.Json System.Text.Json.JsonReaderOptions
System.Text.Json System.Text.Json.JsonReaderState
System.Text.Json System.Text.Json.Utf8JsonReader
System.Text.Json System.Text.Json.JsonWriterOptions
System.Text.Json System.Text.Json.JsonWriterState
System.Text.Json System.Text.Json.Utf8JsonWriter
System.Security.Principal.Windows System.Security.Principal .WindowsAccountType
System.Security.Cryptography.Primitives System.Security.Cryptography .PbeEncryptionAlgorithm
System.Security.Cryptography.Primitives System.Security.Cryptography .PbeParameters
System.Security.Cryptography.Algorithms System.Security.Cryptography.AesCcm
System.Security.Cryptography.Algorithms System.Security.Cryptography.AesGcm
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.CornerRadius
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.DurationType
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Duration
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.GridUnitType
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.GridLength
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Thickness
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.LayoutCycleException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Markup .XamlParseException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Automation .ElementNotAvailableException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Automation .ElementNotEnabledException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Matrix
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Media3D.Matrix3D
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Animation.KeyTime
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Animation .RepeatBehaviorType
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Animation .RepeatBehavior
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Controls.Primitives .GeneratorPosition
System.Runtime.WindowsRuntime Windows.UI.Color
System.Runtime.WindowsRuntime Windows.Foundation.Point
System.Runtime.WindowsRuntime Windows.Foundation.Rect
System.Runtime.WindowsRuntime Windows.Foundation.Size
System.Runtime.WindowsRuntime System.WindowsRuntimeSystemExtensions
System.Runtime.WindowsRuntime System.IO .WindowsRuntimeStorageExtensions
System.Runtime.WindowsRuntime System.IO.WindowsRuntimeStreamExtensions
System.Runtime.WindowsRuntime System.Threading.DispatcherQueueHandler
System.Runtime.WindowsRuntime System.Threading.DispatcherQueuePriority
System.Runtime.WindowsRuntime System.Runtime.InteropServices .WindowsRuntime.AsyncInfo
System.Runtime.WindowsRuntime System.Runtime.InteropServices .WindowsRuntime.WindowsRuntimeBuffer
System.Runtime.WindowsRuntime System.Runtime.InteropServices .WindowsRuntime .WindowsRuntimeBufferExtensions
System.Runtime.Serialization System.Runtime.Serialization .ISerializationSurrogateProvider
System.Runtime.Loader System.Runtime.Loader .AssemblyDependencyResolver
System.Runtime.Intrinsics System.MidpointRounding
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector64
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector64<T>
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector128
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector128<T>
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector256
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector256<T>
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86 .FloatComparisonMode
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Aes
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Avx
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Avx2
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Bmi1
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Bmi2
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Fma
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Lzcnt
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Pclmulqdq
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Popcnt
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse2
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse3
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse41
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse42
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Ssse3
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Aes
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Base
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Sha1
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64 .Sha256
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Simd
System.Runtime.InteropServices System.Runtime.CompilerServices .IDispatchConstantAttribute
System.Runtime.InteropServices System.Runtime.InteropServices .StandardOleMarshalObject
System.Runtime.InteropServices System.Runtime.InteropServices .NativeLibrary
System.Runtime.Extensions System.AppDomainSetup
System.Runtime.Extensions System.Security.IStackWalk
System.Runtime.Extensions System.Security.PermissionSet
System.Runtime.Extensions System.Security.Permissions .PermissionState
System.Runtime.Extensions System.Runtime.ProfileOptimization
System.Runtime System.ArgIterator
System.Runtime System.IAsyncDisposable
System.Runtime System.Index
System.Runtime System.Range
System.Runtime System.Text.Rune
System.Runtime System.Text.StringRuneEnumerator
System.Runtime System.Globalization.ISOWeek
System.Runtime System.Threading.Tasks.Sources .ManualResetValueTaskSourceCore<TResult>
System.Runtime System.Runtime.Remoting.ObjectHandle
System.Runtime System.Runtime.CompilerServices .AsyncIteratorStateMachineAttribute
System.Runtime System.Runtime.CompilerServices .CallerArgumentExpressionAttribute
System.Runtime System.Collections.Generic .IAsyncEnumerable<T>
System.Runtime System.Collections.Generic .IAsyncEnumerator<T>
System.Private.CoreLib System.IAsyncDisposable
System.Private.CoreLib System.Index
System.Private.CoreLib System.Range
System.Private.CoreLib System.Text .StringBuilder+ChunkEnumerator
System.Private.CoreLib System.Text.Rune
System.Private.CoreLib System.Text.SpanRuneEnumerator
System.Private.CoreLib System.Text.StringRuneEnumerator
System.Private.CoreLib System.Globalization.ISOWeek
System.Private.CoreLib System.Buffers.OperationStatus
System.Private.CoreLib System.Buffers.StandardFormat
System.Private.CoreLib System.Buffers.Text.Utf8Formatter
System.Private.CoreLib System.Buffers.Text.Utf8Parser
System.Private.CoreLib System.Buffers.Binary.BinaryPrimitives
System.Private.CoreLib System.Threading.Tasks.Sources .ManualResetValueTaskSourceCore<TResult>
System.Private.CoreLib System.Runtime.Remoting.ObjectHandle
System.Private.CoreLib System.Runtime.Loader .AssemblyDependencyResolver
System.Private.CoreLib System.Runtime.CompilerServices .AsyncIteratorMethodBuilder
System.Private.CoreLib System.Runtime.CompilerServices .AsyncIteratorStateMachineAttribute
System.Private.CoreLib System.Runtime.CompilerServices .CallerArgumentExpressionAttribute
System.Private.CoreLib System.Runtime.CompilerServices .ConfiguredCancelableAsyncEnumerable<T>
System.Private.CoreLib System.Runtime.CompilerServices .ConfiguredCancelableAsyncEnumerable<T >+Enumerator
System.Private.CoreLib System.Runtime.Intrinsics.Vector64
System.Private.CoreLib System.Runtime.Intrinsics.Vector128
System.Private.CoreLib System.Runtime.Intrinsics.Vector256
System.Private.CoreLib System.Runtime.Intrinsics.X86.Bmi1+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Bmi2+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Lzcnt+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Popcnt+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse2+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse41+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse42+X64
System.Private.CoreLib System.Runtime.InteropServices .DllImportResolver
System.Private.CoreLib System.Runtime.InteropServices .NativeLibrary
System.Private.CoreLib System.Runtime.InteropServices .ComActivationContext
System.Private.CoreLib System.Runtime.InteropServices .ComActivationContextInternal
System.Private.CoreLib System.Runtime.InteropServices .ComActivator
System.Private.CoreLib System.Runtime.InteropServices .ComEventInterfaceAttribute
System.Private.CoreLib System.Runtime.InteropServices .DefaultParameterValueAttribute
System.Private.CoreLib System.Diagnostics.DebugProvider
System.Private.CoreLib System.Collections.Generic .IAsyncEnumerable<T>
System.Private.CoreLib System.Collections.Generic .IAsyncEnumerator<T>
System.Private.CoreLib Internal.Resources.PRIExceptionInfo
System.Private.CoreLib Internal.Resources .WindowsRuntimeResourceManagerBase
System.Private.CoreLib Internal.Threading.Tasks .AsyncCausalitySupport
System.Private.CoreLib Internal.Runtime.InteropServices .WindowsRuntime.ExceptionSupport
System.ObjectModel System.Reflection.ICustomTypeProvider
System.ObjectModel System.ComponentModel .TypeConverterAttribute
System.ObjectModel System.ComponentModel .TypeDescriptionProviderAttribute
System.ObjectModel System.Windows.Markup .ValueSerializerAttribute
System.Net.Sockets System.Net.Sockets.SafeSocketHandle
System.Memory System.Text.SpanRuneEnumerator
System.Memory System.Buffers.SequenceReader<T>
System.Memory System.Buffers.SequenceReaderExtensions
System System.StringNormalizationExtensions
System System.Reflection.ICustomTypeProvider
System System.Windows.Markup .ValueSerializerAttribute
System System.Runtime.InteropServices .StandardOleMarshalObject
System System.Diagnostics.ConsoleTraceListener
System System.Diagnostics .XmlWriterTraceListener
System System.Diagnostics.StackFrameExtensions
System System.Security.SecureStringMarshal
System System.Net.Sockets .SocketReceiveFromResult
System System.Net.Sockets .SocketReceiveMessageFromResult
System System.Net.Sockets.SocketTaskExtensions
System.Diagnostics.TextWriterTraceListener System.Diagnostics.ConsoleTraceListener
System.Diagnostics.TextWriterTraceListener System.Diagnostics .XmlWriterTraceListener
System.Data System.Xml.XmlDataDocument
System.Data System.Data.Common.DbColumn
System.Data System.Data.Common .DbDataReaderExtensions
System.Data System.Data.Common .IDbColumnSchemaGenerator
System.Core System.Security.Cryptography.AesCng
System.Core System.Security.Cryptography.DSACng
System.Core System.Security.Cryptography .TripleDESCng
System.Core System.Security.Cryptography.ECCurve
System.Core System.Security.Cryptography .ECParameters
System.Core System.Security.Cryptography.ECPoint
System.Core System.Security.Cryptography .IncrementalHash
System.Core System.Security.Cryptography .X509Certificates.CertificateRequest
System.Core System.Security.Cryptography .X509Certificates .DSACertificateExtensions
System.Core System.Security.Cryptography .X509Certificates .SubjectAlternativeNameBuilder
System.Core System.Security.Cryptography .X509Certificates.X509SignatureGenerator
System.ComponentModel.TypeConverter System.ComponentModel.VersionConverter
System.ComponentModel.Primitives System.ComponentModel .InvalidAsynchronousStateException
System.ComponentModel.DataAnnotations System.ComponentModel.DataAnnotations .AssociatedMetadataTypeTypeDescriptionPr ovider
System.ComponentModel.DataAnnotations System.ComponentModel.DataAnnotations .MetadataTypeAttribute
System.ComponentModel.Annotations System.ComponentModel.DataAnnotations .AssociatedMetadataTypeTypeDescriptionPr ovider
System.ComponentModel.Annotations System.ComponentModel.DataAnnotations .MetadataTypeAttribute
mscorlib System.TupleExtensions
mscorlib System.AppDomainSetup
mscorlib System.Globalization .GlobalizationExtensions
mscorlib System.Threading.ThreadPoolBoundHandle
mscorlib System.Threading.PreAllocatedOverlapped
mscorlib System.Runtime.ProfileOptimization
mscorlib System.Runtime.Remoting.ObjectHandle
mscorlib System.Runtime.CompilerServices .RuntimeFeature
mscorlib System.Runtime.CompilerServices .IsByRefLikeAttribute
mscorlib System.Runtime.CompilerServices.ITuple
mscorlib System.Runtime.CompilerServices .IsReadOnlyAttribute
mscorlib System.Runtime.CompilerServices .TupleElementNamesAttribute
mscorlib System.Runtime.CompilerServices .IDispatchConstantAttribute
mscorlib System.Runtime.InteropServices .RuntimeInformation
mscorlib System.Runtime.InteropServices .Architecture
mscorlib System.Runtime.InteropServices .OSPlatform
mscorlib System.Reflection.Emit.DynamicILInfo
mscorlib System.Security.IStackWalk
mscorlib System.Security.PermissionSet
mscorlib System.Security.Permissions .PermissionState
mscorlib System.Security.Principal .WindowsAccountType
mscorlib System.Diagnostics.Tracing .EventSourceCreatedEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic.Collection
Microsoft.VisualBasic Microsoft.VisualBasic.CompareMethod
Microsoft.VisualBasic Microsoft.VisualBasic.ComClassAttribute
Microsoft.VisualBasic Microsoft.VisualBasic.ControlChars
Microsoft.VisualBasic Microsoft.VisualBasic.DateAndTime
Microsoft.VisualBasic Microsoft.VisualBasic.Information
Microsoft.VisualBasic Microsoft.VisualBasic .MyGroupCollectionAttribute
Microsoft.VisualBasic Microsoft.VisualBasic.VariantType
Microsoft.VisualBasic Microsoft.VisualBasic .VBFixedArrayAttribute
Microsoft.VisualBasic Microsoft.VisualBasic .VBFixedStringAttribute
Microsoft.VisualBasic Microsoft.VisualBasic.VBMath
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO.FileSystem
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .DeleteDirectoryOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .RecycleOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .SearchOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .UICancelOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO.UIOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .MalformedLineException
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .SpecialDirectories
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .TextFieldParser
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO.FieldType
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .BooleanType
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .DecimalType
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .Versioned
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .DoubleType
Microsoft.VisualBasic Microsoft.VisualBasic .ApplicationServices.StartupEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic .ApplicationServices .StartupNextInstanceEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic .ApplicationServices .UnhandledExceptionEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic.Devices .NetworkAvailableEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CallType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Collection
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompareMethod
Microsoft.VisualBasic.Core Microsoft.VisualBasic.ComClassAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Constants
Microsoft.VisualBasic.Core Microsoft.VisualBasic.ControlChars
Microsoft.VisualBasic.Core Microsoft.VisualBasic.DateAndTime
Microsoft.VisualBasic.Core Microsoft.VisualBasic .HideModuleNameAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Information
Microsoft.VisualBasic.Core Microsoft.VisualBasic .MyGroupCollectionAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Strings
Microsoft.VisualBasic.Core Microsoft.VisualBasic.VariantType
Microsoft.VisualBasic.Core Microsoft.VisualBasic .VBFixedArrayAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic .VBFixedStringAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.VBMath
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO.FileSystem
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .DeleteDirectoryOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .RecycleOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .SearchOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .UICancelOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO.UIOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .MalformedLineException
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .SpecialDirectories
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .TextFieldParser
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO.FieldType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .BooleanType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Conversions
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .DecimalType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .DesignerGeneratedAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate0
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate1
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate2
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate3
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate4
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate5
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate6
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate7
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .IncompleteInitialization
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .NewLateBinding
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .ObjectFlowControl
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .ObjectFlowControl+ForLoopControl
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Operators
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .OptionCompareAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .OptionTextAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .ProjectData
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .StandardModuleAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .StaticLocalInitFlag
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Utils
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Versioned
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .DoubleType
Microsoft.VisualBasic.Core Microsoft.VisualBasic .ApplicationServices.StartupEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic .ApplicationServices .StartupNextInstanceEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic .ApplicationServices .UnhandledExceptionEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Devices .NetworkAvailableEventArgs

Shotgun Surgery: What It Is and How to Stop It

I really love the name “shotgun surgery” for describing a code smell.  It’s sort of an interesting mix of aggressive and comical, and so it paints a memorable picture.  I kind of picture Elmer Fudd blasting away at a codebase and then declaring “ship it” and doing some kind of one-click production push.

But misappropriating Looney Tunes aside, what actually is shotgun surgery?

Well, it’s a specific code smell in your codebase.  Shotgun surgery happens when you have to make many changes in your codebase to achieve seemingly simple tasks.  Often, you’ll find yourself making changes to code that seems pretty similar, either copy-pasted directly, or else of similar intent.

Continue reading Shotgun Surgery: What It Is and How to Stop It

5 Tips to Help You Visualize Code

5 Tips to Help You Visualize Code

Source code doesn’t have any physical weight — at least not until you print it out on paper.  But it carries a lot of cognitive weight.  It starts off simply enough. But before long, you have files upon files, folders upon folders, and more lines of code than you can ever keep straight.  This is where the quest to visualize code comes in.

The solution file and namespaces organization make for a pretty unhelpful visualization aid.  But that’s nothing against those tools. It’s just not what they’re for.  Nevertheless, if the only way you attempt to visualize code involves staring at hierarchical folders, you’re gonna have a bad time.

How do most people handle this?  Well, they turn to whiteboards, formal design documents, architecture diagrams, and the like.  This represents a much more powerful visual aid, and it tends to serve as table stakes of meaningful software development.

But it’s a siren song.  It’s a trap.

Why?  Well, as I’ve discussed previously, those visualization aids just represent someone’s cartoon of what they think the code will look like when complete.  You draw up a nice layer-cake architecture, and you wind up with something that looks more like six tumbleweeds glued to a barbed wire fence.  Those visual aids are great…for visualizing what everyone wishes your code looked like.

What I want to talk about today are strategies to visualize code — your actual code, as it exists.

Continue reading 5 Tips to Help You Visualize Code

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!

should you aim for 100% test code coverage?

Should You Aim for 100 Percent Test Coverage?

Test coverage serves as one of the great lightning rods in the world of software development.  First, people ask whether it makes for a good metric at all.  Then they ask, if you want to use it as a metric, should you go for 100 percent coverage?  If not, what percentage should you go for? Maybe 42 percent, since that’s the meaning of life?

I don’t mean to trivialize an important discussion.  But sometimes it strikes me that this one could use some trivializing.  People dig in and draw battle lines over it, and counterproductive arguments often ensue.  It’s strange how fixated people get on this.

I’ll provide my take on the matter here, after a while.  But first, I’d like to offer a somewhat more philosophical look at the issue (hopefully without delving into overly abstract navel-gazing along the lines of “What even is a test, anyway, in the greater scheme of life?”)

What Does “Test Coverage” Measure?

First of all, let’s be very clear about what this metric measures.  Many in the debate — particularly those on the “less is more” side of it — quickly point out that test coverage does not measure the quality of the tests.  “You can have 100 percent coverage with completely worthless tests,” they’ll point out.  And they’ll be completely right.

To someone casually consuming this metric, the percentage can easily mislead.  After all, 100 percent coverage sounds an awful lot like 100 percent certainty.  If you hired me to do some work on your car and I told you that I’d done my work “with 100 percent coverage,” what would you assume?  I’m guessing you’d assume that I was 100 percent certain nothing would go wrong and that I invited you to be equally certain.  Critics of the total coverage school of thought point to this misunderstanding as a reason not to pursue that level of test coverage.  But personally, I just think it’s a reason to clarify definitions.

Continue reading Should You Aim for 100 Percent Test Coverage?

how to use ndepends trend charts

How to Use NDepend’s Trend Charts

Imagine a scene for a moment.  A year earlier, a corporate VP spun up a major software project for his organization.  He brought a slew of his organization’s software developers into the project.  But he also needed to add more staff in the form of contractors.

This strained the budget, so he cut a few corners in terms of team member experience.  The VP reasoned that he could make up for this with strategic use of experienced architects up front.  Those architects would prototype good patterns and make it so the less seasoned contractors could just kind of paint by numbers.  The architects spent a few months doing just that and handed the work off to the contractors.

Fast forward to the present.  Now a consultant sits in a nice office, explaining to a beleaguered VP how they got so far behind schedule.  I can picture this scene quite easily because organizations hire me to be this consultant.  I live this scene over and over again.
Continue reading How to Use NDepend’s Trend Charts

static analysis issue management gets a boost

Static Analysis Issue Management Gets a Boost

Years ago, I led a team of software developers.  We owned an eclectic portfolio of software real estate.  It included some Winforms, Webforms, MVC, and even a bit of WPF sprinkled into the mix.  And, as with any eclectic neighborhood, the properties came in a variety of ages and states of repair.

Some of this code depended on a SQL Server database that had a, let’s just say, casual relationship with normalization.  Predictably, this caused maintenance struggles.  But, beyond that, it caused a credibility gap when we spoke to non-technical stakeholders.  “What do you mean you can’t give a definitive answer to how many sales we made last year?”  “Well,” I’d try to explain, “I can’t say for sure because the database doesn’t explicitly define the concept of a sale.”

Flummoxed by the mutual frustration, I tried something a bit different.  Since I couldn’t easily explain the casual, implied relationships in the database, I decided to do a show and tell.  First, I went out and found a static analyzer for database schema.  Then, I brought in some representative stakeholders and said, “watch this.”  With a flourish (okay, not really), I turned the analyzer loose on the schema.

While they didn’t grok my analogies, they the tens of thousands of warnings and errors made an impression.  In fact, it sort of terrified them.  But this did bridge the credibility gap and show them that we all had some work to do.  Mission accomplished.

Static Analyzer Issues

I engaged in something of a relationship hack with my little ploy.  You see, I know how this static analyzer would behave because I know how all of them tend to behave.  They earn their keep by carpet bombing your codebase with violations and warnings.  Out of the box, they overwhelm, and then they leave it to you to dial it back.  Truly, you can take this behavior to the bank.

So I knew that this creaky database would trigger thousands upon thousands of violations.  And then I just sat back waiting for the “magic” to happen.

I mention all of this to paint a picture of how static analyzers typically regard the concept of “issue.”  All categories of severity and priority generally roll up into this catch-all term, and it then refers to the itemized list of everything.  Your codebase has issues and it has lots of them.  This is how the tool earns its mindshare and keep — by proving how much it can surface, and then doing so.

Thus you might define the concept simply as “all that stuff the static analyzer finds.”

Continue reading Static Analysis Issue Management Gets a Boost

exploring technical debt codebase

Exploring the Technical Debt In Your Codebase

Recently, I posted about how the new version of NDepend lets you compute tech debt.  In that post, I learned that I had earned a “B” out of the box.  With 40 minutes of time investment, I could make that an “A.”  Not too shabby!

In that same post, I also talked about the various settings in and around “debt settings.”  With debt settings, you can change units of debt (time, money), thresholds, and assumptions of working capacity.  For folks at the intersection of tech and business, this provides an invaluable way to communicate with the business.

But I really just scratched the surface with that mention.  You’re probably wondering what this looks like in more detail.  How does this interact with the NDepend features you already know and love?

Well, today, I’d like to take a look at just that.

To start, let’s look at the queries and rules explorer in some detail.

Introducing Quality Gates

Take a look at this screenshot, and you’ll notice some renamed entries, some new entries, and some familiar ones.

In the past, “Code Smells” and “Code Regressions” had the names “Code Quality” and “Code Quality Regression,” respectively.  With that resolved, the true newcomers sit on top: Quality Gates and Hot Spots.  Let’s talk about quality gates.

Continue reading Exploring the Technical Debt In Your Codebase

the relationship between team size and code quality

The Relationship Between Team Size and Code Quality

Over the last few years, I’ve had the occasion to observe lots of software teams.  These teams come in all shapes and sizes, as the saying goes.  And, not surprisingly, they produce output that covers the entire spectrum of software quality.

It would hardly make headline news to cite team members’ collective skill level and training as a prominent factor in determining quality level.  But what else affects it?  Does team size?  Recently, I found myself pondering this during a bit of downtime ahead of a meeting.

Continue reading The Relationship Between Team Size and Code Quality