CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Patrick Smacchia [MVP C#]

May 2008 - Posts

  • .NET 3.5 SP1 Beta: Changes Overview

     

    Scott Hanselman just published a blog post where he used NDepend to see changes in the code of .NET 3.5 SP1 beta (that has just been released). Let’s expose the exhaustive list of differences:

     

    Summary:

    # IL instructions:    8 598 933 to 8 589 008      (-9 925   -0.1%)

    # Assemblies:    112

    # Namespaces:    919 to 929      (+10   +1.1%)

    # Types:    39 988 to 40 402      (+414   +1%)

    # Methods:    387 421 to 385 253      (-2 168   -0.6%)

    # Fields:    241 567 to 246 320      (+4 753   +2%)

     

    636 new public methods:  

    SELECT METHODS WHERE IsPublic AND WasAdded

     

    57 new public types:  

    SELECT TYPES WHERE IsPublic AND WasAdded

     

    3.137 public methods removed (this number is biased by the fact that a public methods can be declared in an internal types): 

    SELECT METHODS WHERE IsPublic AND WasRemoved

     

    8 public types removed

    SELECT TYPES WHERE IsPublic AND WasRemoved

     

    5.623 methods where code was changed

    SELECT METHODS WHERE CodeWasChanged

     

    2.024 types where code was changed

    SELECT TYPES WHERE CodeWasChanged

     

    The list of assemblies we consider is made of 112 assemblies

     

    Here is a 9000x1200 poster where methods where code was changed are located in blue (we degraded the quality to have a 2MB image file instead of 13MB):

     

     

    And here is a list of coupling update for assemblies:

    1. A blue cell means: {the X Assembly} is using {the Y assembly}.
    2. Weight of a blue cell means: W types of the {the X Assembly} are used by {the Y assembly}.
    3. A green cell means: {the Y Assembly} is used by {the X assembly}.
    4. Weight of a green cell means: W types of the {the Y Assembly} are using {the X assembly}.
    5. A black cell means: {the X Assembly} and {the Y assembly} are using each others.
    6. A red tick on a cell means: the coupling has been changed.
    7. A red tick with a plus on a cell means: the dependency has been created.
    8. A red tick with a minus on a cell means: the dependency has been removed.
    9. An assembly name underlined means that its code has been changed.

     

     

  • 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