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

 

 

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Matt

    Can you set it up so that VS will not even compile if any of these rules are broken?

    That would be the ultimate way to enforce strict coding standards.

  • http://opeyixa.com/qoxxyo/5.html Pharmc885

    Very nice site!