Patrick Smacchia [MVP C#]

Sponsors

The Lounge

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Write Active Conventions on your Code Base

Recently, both Glenn Block and Ayende wrote about how to define some sort of active conventions about the code base. The idea is great! The proposed implementation is based on the framework System.Reflection used from some unit tests to assert some properties on some elements of the code base. Not taking account the problems encountered with Reflection, this approach suffers the same problem than writing custom rules for FxCop: there is a lot of noise/friction and it takes dozens of minutes just to write a single convention and make sure it is working correctly.

 

The language Code Query Language supported by NDepend has been especially designed to write active conventions / rules/ constraints in a frictionless way. The purpose of this post is to enumerate popular active code conventions written with CQL. These conventions are related to a rich set of area including: Dependencies, Structure, Quality, Metrics, Evolution, Diff / Changes, Coverage, Purity / Side-Effects / Immutability, Encapsulation, Call Graph, Separation of Concerns, Componentization, Layering, Coupling / Dead / Unused Code, Naming Conventions…

 

Dependencies / Structure

 

As the name NDepend suggests, dependencies is a major concern of the tool. CQL lets write all sorts of conventions about which part of the code is allowed to use or not which other part. Suppose you are in charge of writing conventions on mscorlib and you don’t want that the namespace Microsoft.Win32 uses the namespace System.Collections. You just have to write the CQL rule:

 

WARN IF Count > 0 IN SELECT NAMESPACES WHERE

IsDirectlyUsing "System.Collections.Generic" AND

NameIs "Microsoft.Win32"

 

I hope that the syntax is slick and concise enough to not require any additional comments. The VisualNDepend UI lets generate such a rule directly from the Dependencies Structure Matrix:

 

 

 

From there, you can write any constraints you can imagine in order to restrict (and thus control) the evolution of the structure of your code base. For example the following constraint warns if the assembly Asm1 uses something else from the assembly Asm2 than the class MyNamespace.Foo:

 

WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "Asm2"

WHERE IsDirectlyUsedBy "Asm1" AND

!NameIs "MyNamespace.Foo"

 

…and the following rules warns if the class MyNamespace.Foo is used by a namespace which name doesn’t satisfy the regex ‘begin with MyNamespace.Internal’:

 

WARN IF Count > 0 IN SELECT NAMESPACES

WHERE IsDirectlyUsing "MyNamespace.Foo" AND

!NameLike "^MyNamespace.Internal"

 

Related CQL conditions: IsDirectlyUsing, IsDirectlyUsedBy, NameIs, FullNameIs, NameLike, FullNameLike

 

More on this here: Keep your code structure clean 

 

 

Quality / Metrics

 

CQL comes with support for 80 code metrics + numerous facilities in VisualNDepend to dig into abnormal values. Suppose that you don’t want that a method have more than 25 lines of code except if it is a Windows Form InitializeComponent() generated method:

 

WARN IF Count > 0 IN SELECT METHODS

WHERE NbLinesOfCode > 25 AND !NameIs "InitializeComponent()"

 

Amongst popular quality metrics supported by NDepend let's quote: PercentageComment, CyclomaticComplexity (computed from code source or IL),  IL Nesting Depth, Size of Instance, Efferent Coupling  

 

The exhaustive description of all metrics is available here.

 

 

Evolution / Changes

 

CQL can be used to rule the diff between any 2 snapshots of a code base taken at different points in time. In other words, one can continuously control the evolution of the code. Suppose you want to avoid breaking changes such as a public method in the older snapshot that doesn’t exist anymore in the newer snapshot:

 

WARN IF Count > 0 IN SELECT METHODS

WHERE IsPublic AND (VisibilityWasChanged OR WasRemoved)

 

More on API breaking changes rules here: Avoid API breaking changes 

 

Mixing CQL code changes conditions and other CQL conditions is a smart way to write rules that should be applied only from a particular milestone. Suppose that from today, all method refactored or added should have a cyclomatic complexity lower than 8:

 

WARN IF Count > 0 IN SELECT METHODS

WHERE CyclomaticComplexity > 8 AND (CodeWasChanged OR WasAdded)

 

More on this possibility here: Ensure the quality of the code that will be developed this year 

 

Related CQL conditions: WasAdded, WasRemoved, CodeWasChanged, CommentsWereChanged, VisibilityWasChanged, WasChanged, BecameObsolete, IsUsedRecently, IsNotUsedAnymore, IsUsedDifferently, IsInNewerBuild, IsInOlderBuild

 

 

Code Coverage by Tests

 

NDepend can import code coverage metrics computed from NCover and Microsoft Visual Studio Team System. For example you can define an attribute on types YourNamespace.FullCoveredAttribute and make sure that all types tagged with this attribute are and will remain 100% covered:

 

WARN IF Count > 0 IN SELECT TYPES

WHERE PercentageCoverage < 100 AND HasAttribute "YourNamespace.FullCoveredAttribute"

 

You might also want to make sure that code that was added or refactored since the last released is 100% covered (a popular agile practice that so far, cannot be strictly applied without CQL):

 

WARN IF Count > 0 IN SELECT METHODS

WHERE PercentageCoverage < 100 AND (CodeWasChanged OR WasAdded)

 

More on this here: Make the most of your test coverage data

 

Related CQL conditions: PercentageCoverage, PercentageBranchCoverage, NbLinesOfCodeCovered, NbLinesOfCodeNotCovered, IsExcludedFromCoverage

 

 

Purity / Side-Effects / Immutability

 

One hot .NET language topics actually is purity, meaning how / why / when states are changing at run-time. When states remain constants, there are no more side-effects and as a result you can assert numerous cool things on your program, such as no corrupted state in a multi-threaded environment. CQL lets write some conventions about the mutability. For example you can define an attribute on types YourNamespace.ImmutableAttribute and make sure that all types tagged with this attribute are immutable, meaning the state of their instance objects won’t changed once created:

 

WARN IF Count > 0 IN SELECT TYPES

WHERE !IsImmutable AND HasAttribute "YourNamespace.ImmutableAttribute"

 

In the same spirit, you might want to enforce that some methods are pure:

 

WARN IF Count > 0 IN SELECT METHODS

WHERE (ChangesObjectState OR ChangesTypeState) AND

HasAttribute "YourNamespace.PureAttribute"

 

…or that all structure are immutable:

 

WARN IF Count > 0 IN SELECT TYPES

WHERE !IsImmutable AND IsStructure

 

You can also restrict write access to a particular field to certain methods:

 

WARN IF Count > 0 IN SELECT METHODS

WHERE IsDirectlyWritingField "YourNamespace.YourClass.m_Field" AND

!FullNameIs "YourNamespace.YourClass.Method1()" AND

!FullNameIs "YourNamespace.YourClass.set_Field(Int32)"

 

More on this here: Immutable Types:  understand their benefits and use them 

 

Related CQL conditions: ChangesObjectState, ChangesTypeState, IsImmutable, IsWritingField, DepthOfIsWritingField, IsDirectlyWritingField

 

 

Optimal Encapsulation

 

CQL comes with several conditions especially designed to pinpoint code elements not optimally encapsulated, such as an internal methods that could be declared private without any compilation break:

 

WARN IF Count > 0 IN SELECT METHODS

WHERE IsInternal AND CouldBePrivate

 

More on this here: Optimal Encapsulation  

 

Related CQL conditions:  CouldBeInternal, CoulBeInternalProtected,  CouldBeProtected, CouldBePrivate, ShouldBePublic, IsPublic, IsInternal, IsProtected, IsPrivate, IsInternalAndProtected, IsInternalOrProtected

 

 

Coupling / Dead / Unused Code

 

With a bit of astute, it is easy to write rules that detects potentially dead code, i.e code that is not used anymore and that can be safely removed. The word potentially is used here because a static analysis tool cannot mathematically detect the exact set of dead code elements. The idea is to detect code elements with no afferent coupling, meaning, not used anywhere in the code. We rely on the fact that the value of the metric Ca (Afferent Coupling) is equal to 0 in such case. For example, to warn if some methods are potentially not used:

 

WARN IF Count > 0 IN SELECT TOP 10 METHODS WHERE

 MethodCa == 0 AND            // Ca=0 -> No Afferent Coupling -> The method is

                              // not used in the context of this application.

 !IsPublic AND                // Public methods might be used by client

                              // applications of your assemblies.

 !IsEntryPoint AND            // Main() method is not used by-design.

 !IsExplicitInterfaceImpl AND // The IL code never explicitely calls

                              // explicit interface methods implementation.

 !IsClassConstructor AND      // The IL code never explicitely calls class ctors.

 !IsFinalizer                 // The IL code never explicitely calls finalizers.

 

More on this here: Code metrics on Coupling, Dead Code, Design Flaws and Re-engineering 

 

Related CQL conditions:  NamespaceCa, MethodCa, TypeCa, FieldCa

 

 

Naming Conventions

 

Because CQL supports regex validation, it is easy to write any naming convention such as: all static fields names should begin with s_:

 

WARN IF Count > 0 IN SELECT FIELDS WHERE !NameLike "^s_" AND IsStatic

 

Or all exception classes should end up with Exception:

 

WARN IF Count > 0 IN SELECT TYPES WHERE

DeriveFrom "System.Exception" AND

!NameLike "Exception$"

 

Related CQL conditions: NameLike, FullNameLike

 

 

Call Graph

 

NDepend is designed not only to handle direct dependencies, but also indirect dependencies. Meaning, you can not only constraint that A cannot use directly B, but also that A cannot used something that is using something that is using B. For example, suppose that you want to make sure that your business objects (contained in YourBusinessObjects namespace) won’t rely directly or indirectly on your data access layer (contained in YourDataAccess namespace).

 

WARN IF Count > 0 IN SELECT NAMESPACES WHERE

IsUsedBy "YourBusinessObjects" AND

NameIs "YourDataAccess"

 

The conditions IsUsedBy and IsUsing could have been actually named IsIndirectlyUsedBy or IsIndirectlyUsing but the way it is now sounds more concise. There are also facilities to get the depth of use. Once you got the trick this opens a wide range of possibilities to re-engineer some code, explained here: Deconstructing Software

 

Related CQL conditions: IsUsing, IsUsedBy, DepthOfIsUsing, DepthOfIsUSedBy

 

Separation of Concern

 

CQL conditions related to dependencies can be used to enforce some sort of separation of concerns. For example, the following CQL rule makes sure that web methods are not using directly DB:

 

WARN IF Count > 0 IN SELECT METHODS WHERE

IsDirectlyUsing "System.Web" AND

IsDirectlyUsing "System.Data.SqlClient"

 

More on this here: Dependencies and Concerns 

 

Componentization, Layering

 

VisualStudio detects dependencies cycles between assemblies but you are stuck if you need to forbid cycles between something more granular than assemblies, aka namespaces or types or methods. Dependencies cycles are strongly related to componentization because you define components especially to layer them, meaning no dependency cycles should exist between components. This limitation leads to a dramatic problem according to me: most .NET developers consider that one component = one assembly and this leads to aberration such as hundreds of VisualStudio projects. This is aberration because assemblies are costly physical things while components should be lightweight logical artefacts.

 

If you consider that namespace of the assembly Foo are actually your components, you can constraint the fact that they should not be entangled in cycles with the rule:

 

WARN IF Count > 0 IN SELECT ASSEMBLIES WHERE

ContainsNamespaceDependencyCycles AND NameIs "Foo"

 

This is equivalent to:

 

WARN IF Count > 0 IN SELECT NAMESPACES FROM ASSEMBLIES "Foo" WHERE

!HasLevel

 

The metric Level is a cool metric that can only be computed if no dependencies cycles exist, hence the equivalence. In the same spirit you might want to constraint that methods of a type or types of a namespaces are layered:

 

WARN IF Count > 0 IN SELECT TYPES WHERE

ContainsMethodDependencyCycles AND NameIs "YourNamespace.YourClass"

 

More on this here: Control components dependencies to gain clean architecture, here Hints on how to componentized code and here Layering, and here The Level metric and the Discourse of Method 

 

Related CQL conditions: ContainsNamespaceDependencyCycles, ContainsTypeDependencyCycles, ContainsMethodDependencyCycles, Level, HasLevel

 

Btw, now that we know the meaning of the conditions IsUsing and IsUsedBy if you don’t want that the class MyNamespace.Foo be involved in a cycle with some other classes you can write the rule:

 

WARN IF Count > 0 IN SELECT TYPES

WHERE IsUsing "MyNamespace.Foo" AND

IsUsedBy "MyNamespace.Foo"

 

 

Miscellaneous

 

Conventions written with CQL cover a wide range of popular scenario and as shown, crossing CQL features open the doors to even more scenario. Several dozens of CQL conventions (applicable to any program) comes with any new NDepend project. Of course, this set of rules can be extended and customized at whim. It is also possible to insert active conventions directly in C# or VB.NET source code as explained here

 

To clarify things, NDepend is especially designed for build process integration and warns when a convention is violated.

 

CQL is currently evolving and more great features are currently under development. Also the post was far from covering all CQL conditions, enumerated in the CQL specification available here. Here are some more rules:

 

// <Name>Fields must be private</Name>

WARN IF Count > 0 IN SELECT FIELDS

WHERE !IsPrivate

 

// <Name>Classes that should be sealed</Name>

WARN IF Count > 0 IN SELECT TYPES

WHERE IsClass AND NbChildren == 0 AND !IsSealed

 

// <Name>Not more than 5% of methods should use boxing or unboxing</Name>

WARN IF Percentage > 5 IN SELECT METHODS WHERE IsUsingBoxing OR IsUsingUnboxing

 

// <Name>Stateless types only made of functions that might be static</Name>

WARN IF Count > 0 IN SELECT TOP 10 TYPES WHERE

SizeOfInst ==0 AND !IsStatic AND !IsGeneric AND !IsInterface

 

 


Posted Sun, May 11 2008 10:10 PM by Patrick Smacchia

[Advertisement]

Comments

on Mon, May 12 2008 4:24 AM

I&#39;ve said it before and I want to say it again. If you are serious about code quality and you want

define business project engineering wrote define business project engineering
on Thu, May 15 2008 1:22 AM

Pingback from  define business project engineering

Elegant Code » Active Conventions with NDepend wrote Elegant Code &raquo; Active Conventions with NDepend
on Sat, Jun 28 2008 5:48 PM

Pingback from  Elegant Code &raquo; Active Conventions with NDepend

Patrick Smacchia [MVP C#] wrote My 100th blog post: Top 5 development practices you should care for
on Wed, Mar 25 2009 3:48 AM

This is a modest number but I am happy to have reached it, especially taking account that I spend weekly

Community Blogs wrote My 100th blog post: Top 5 development practices you should care for
on Wed, Mar 25 2009 3:59 AM

This is a modest number but I am happy to have reached it, especially taking account that I spend weekly

Pharmc885 wrote re: Write Active Conventions on your Code Base
on Sat, Jun 6 2009 5:08 AM

Very nice site!

Patrick Smacchia [MVP C#] wrote Agile Behavior: Nurture Knowledge Database
on Sun, Jun 21 2009 5:22 AM

Normal 0 false false false EN-US X-NONE X-NONE Code Query Language (CQL) can be used to write all sorts

Patrick Smacchia [MVP C#] wrote Agile Behavior: Nurture Knowledge Database
on Sun, Jun 21 2009 5:27 AM

Normal 0 false false false EN-US X-NONE X-NONE Code Query Language (CQL) can be used to write all sorts

Patrick Smacchia [MVP C#] wrote Rambling on Cyclomatic Complexity
on Tue, Oct 6 2009 10:50 AM

Normal 0 21 false false false FR X-NONE X-NONE After the number of Lines of Code , the Cyclomatic Complexity

Patrick Smacchia [MVP C#] wrote The code is the truth, but it is not the whole truth
on Wed, Nov 4 2009 9:00 AM

In a recent interview from Grady Booch , co-creator of UML, Grady said: When Jim, Ivar, and I began our

Patrick Smacchia [MVP C#] wrote NDepend v3 is now 100% integrated in Visual Studio.
on Thu, Jan 28 2010 4:22 AM

First of all, if you are like me, you certainly don’t want another Visual Studio extension that

Add a Comment

(required)  
(optional)
(required)  
Remember Me?