Avoid adding instance fields to a type that already had many instance fields

Rule ND1107 Code Smells Regression

Runs only in diff mode (baseline defined). Lists types that already had more than 15 fields at baseline and got new ones. Such types are hard to maintain, symptom of too many responsibilities. Constants, static-readonly fields and enumerations are not counted.

How to fix

Group subsets of fields into smaller types and dispatch the methods logic into them.

Estimated Debt: 10 minutes per field 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 instance fields to a type that already had many instance fields</Name>
// <Id>ND1107:AvoidAddingInstanceFieldsToATypeThatAlreadyHadManyInstanceFields</Id>
warnif count > 0 

let getFieldsProc = new Func<IType, IList<IField>>(
   t0 => t0.Fields.Where(f => 
          !f.IsLiteral &&
          !f.IsGeneratedByCompiler &&
          !f.IsStatic).ToArray()) 

from t in JustMyCode.Types where
  
 !t.IsEnumeration &&
  t.IsPresentInBothBuilds()

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

  let oldFields = getFieldsProc(t.OlderVersion())
  where oldFields.Count > 15

  let newFields = getFieldsProc(t)
  where newFields.Count > oldFields.Count

  let addedFields = newFields.Where(f => f.WasAdded())
  let removedFields = oldFields.Where(f => f.WasRemoved())

  orderby addedFields.Count() descending

select new { 
   t,
   nbOldFields = oldFields.Count,
   nbNewFields = newFields.Count,
   addedFields,
   removedFields,

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

}

//<Expl>
// The *{0}* previously contained {1} instance fields 
// in the baseline and now has {2} instance fields.
// 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 15 fields at baseline and got new ones. Such types are hard to
// maintain, symptom of too many responsibilities. *Constants*,
// *static-readonly* fields and enumerations are not counted.
//</Desc>

//<HowToFix>
// Group subsets of fields into smaller types and dispatch the methods logic
// into them.
//
// Estimated Debt: 10 minutes per field added. Severity is **High**: fix
// these issues **now**, before such code gets released in production.
//</HowToFix>

Code Smells Regression: all 9 items