Avoid adding methods to a type that already had many methods

Rule ND1106 Code Smells Regression

Runs only in diff mode (baseline defined). Lists types that already had more than 40 methods at baseline and got new ones. Such types are hard to maintain, symptom of too many responsibilities. Constructors and compiler-generated methods are not counted.

How to fix

Split the type into smaller types implementing the same logic together.

Estimated Debt: 10 minutes per method added. Severity is High: fix these issues now, before such code gets released in production.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Avoid adding methods to a type that already had many methods</Name>
// <Id>ND1106:AvoidAddingMethodsToATypeThatAlreadyHadManyMethods</Id>
warnif count > 0 

// Don't count constructors and methods generated by the compiler!
let getMethodsProc = new Func<IType, IList<IMethod>>(
   t0 => t0.Methods.Where(m =>
      //!m.IsConstructor && !m.IsClassConstructor && // ctor/cctor not enumerated through IType.Methods
      !m.IsGeneratedByCompiler).ToArray()) 


from t in JustMyCode.Types where
  
  t.NbMethods > 40 &&   // Just here for optimization

  t.IsPresentInBothBuilds()

  // Optimization: fast discard of non-relevant types
  where t.OlderVersion().NbMethods > 40

  let oldMethods = getMethodsProc(t.OlderVersion())
  where oldMethods.Count > 40

  let newMethods = getMethodsProc(t)
  where newMethods.Count > oldMethods.Count

  let addedMethods = newMethods.Where(m => m.WasAdded())
  let removedMethods = oldMethods.Where(m => m.WasRemoved())

  orderby addedMethods.Count() descending

select new { 
   t,
   nbOldMethods = oldMethods.Count,
   nbNewMethods = newMethods.Count,
   addedMethods,
   removedMethods,

   Debt           = (10*addedMethods.Count()).ToMinutes().ToDebt(),
   AnnualInterest =     addedMethods.Count().Linear(
          1,       Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, 
          100,  4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest()
}

//<Expl>
// The *{0}* previously contained {1} methods 
// in the baseline and now has {2} methods.
// This increase is indicative of excessive complexity, 
// suggesting the necessity of breaking it down into multiple 
// smaller implementations, each dedicated to a single 
// responsibility.
//</Expl>

//<Desc>
// Runs only in *diff mode* (baseline defined). Lists types that already had
// more than 40 methods at baseline and got new ones. Such types are hard to
// maintain, symptom of too many responsibilities. Constructors and
// compiler-generated methods are not counted.
//</Desc>

//<HowToFix>
// Split the type into smaller types implementing the same logic together.
//
// Estimated Debt: 10 minutes per method added. Severity is **High**: fix
// these issues **now**, before such code gets released in production.
//</HowToFix>

Code Smells Regression: all 9 items