feat: tighten CBO coupling thresholds to industry standards

Updated CBO (Coupling Between Objects) analysis to use industry-standard
thresholds for more accurate code quality assessment:

**CBO Risk Thresholds:**
- Low Risk: CBO ≤ 3 (was ≤ 5)
- Medium Risk: 3 < CBO ≤ 7 (was 5 < CBO ≤ 10)
- High Risk: CBO > 7 (was > 10)

**Coupling Ratio Thresholds:**
- Low: 5% of classes (was 10%)
- Medium: 15% of classes (was 30%)
- High: 30% of classes (was 50%)

**Coupling Penalties:**
- Low: 6 points (was 5)
- Medium: 12 points (was 10)
- High: 20 points (was 16, now aligned with other high penalties)

These changes make coupling detection more strict and aligned with industry
best practices, helping identify maintainability issues earlier.

**Files Updated:**
- domain/analyze.go: Updated coupling ratio thresholds and penalties
- domain/cbo.go: Updated default CBO thresholds with industry standard comments
- internal/analyzer/cbo.go: Updated default CBO options
- domain/analyze_test.go: Updated test expectations for new thresholds
- internal/analyzer/cbo_test.go: Updated test expectations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DaisukeYoda
2025-10-05 17:27:38 +09:00
parent 4c9d63a3fc
commit 182bac8b71
5 changed files with 37 additions and 37 deletions

View File

@@ -25,12 +25,12 @@ const (
DuplicationPenaltyLow = 6
// CBO coupling thresholds and penalties
CouplingRatioHigh = 0.5
CouplingRatioMedium = 0.3
CouplingRatioLow = 0.1
CouplingPenaltyHigh = 16
CouplingPenaltyMedium = 10
CouplingPenaltyLow = 5
CouplingRatioHigh = 0.30 // 30% or more classes with high coupling
CouplingRatioMedium = 0.15 // 15-30% classes with high coupling
CouplingRatioLow = 0.05 // 5-15% classes with high coupling
CouplingPenaltyHigh = 20 // Aligned with other high penalties
CouplingPenaltyMedium = 12 // Aligned with other medium penalties
CouplingPenaltyLow = 6 // Aligned with other low penalties
// Maximum penalties
MaxDeadCodePenalty = 20

View File

@@ -167,21 +167,21 @@ func TestAnalyzeSummary_CalculateHealthScore(t *testing.T) {
name: "typical 74 score case",
summary: domain.AnalyzeSummary{
AverageComplexity: 7.0, // -6
CodeDuplication: 15.0, // -12 (new: Medium-High range)
CodeDuplication: 15.0, // -12 (Medium-High range)
CBOClasses: 10,
HighCouplingClasses: 2, // -5
HighCouplingClasses: 2, // 20% ratio: -12 (new: Medium penalty)
DepsEnabled: true,
DepsMainSequenceDeviation: 1.0, // -2
ArchEnabled: true,
ArchCompliance: 0.125, // -7
},
expectedScore: 68, // Updated: 100-6-12-5-2-7 = 68
expectedGrade: "C", // Updated from B
expectedScore: 61, // Updated: 100-6-12-12-2-7 = 61
expectedGrade: "C", // 61 is in C range (55-69)
expectError: false,
expectedComplexityScore: 70, // 100 - (6/20)*100 = 70
expectedDeadCodeScore: 100, // No dead code
expectedDuplicationScore: 40, // Updated: 100 - (12/20)*100 = 40
expectedCouplingScore: 69, // 100 - (5/16)*100 = 69 (rounded)
expectedDuplicationScore: 40, // 100 - (12/20)*100 = 40
expectedCouplingScore: 40, // 100 - (12/20)*100 = 40 (new penalty)
expectedDependencyScore: 83, // 100 - (2/12)*100 = 83 (rounded)
expectedArchitectureScore: 12, // 100 - (7/8)*100 = 12 (rounded)
},
@@ -230,7 +230,7 @@ func TestAnalyzeSummary_CalculateHealthScore(t *testing.T) {
AverageComplexity: 25.0, // -20
CodeDuplication: 50.0, // -20
CBOClasses: 10,
HighCouplingClasses: 6, // -16
HighCouplingClasses: 6, // 60% ratio: -20 (new: High penalty)
DeadCodeCount: 100,
CriticalDeadCode: 50, // -20 (capped)
DepsEnabled: true,
@@ -239,24 +239,24 @@ func TestAnalyzeSummary_CalculateHealthScore(t *testing.T) {
ArchEnabled: true,
ArchCompliance: 0.0, // -8
},
expectedScore: 10, // Floor at 10
expectedScore: 10, // Floor at 10 (actual: 100-20-20-20-20-8-8 = 4, floored to 10)
expectedGrade: "F",
expectError: false,
},
{
name: "grade A threshold",
summary: domain.AnalyzeSummary{
AverageComplexity: 4.0, // -0
CodeDuplication: 5.0, // -12 (new: 3-10% = Medium penalty)
CBOClasses: 10,
HighCouplingClasses: 1, // -5
AverageComplexity: 4.0, // -0 (low complexity)
CodeDuplication: 2.0, // -0 (below 3% threshold)
CBOClasses: 20,
HighCouplingClasses: 2, // 10% ratio: -6 (new: Low penalty)
DepsEnabled: true,
DepsTotalModules: 10,
DepsMaxDepth: 4, // Expected ~4, so no penalty
ArchEnabled: true,
ArchCompliance: 0.9, // -1 (rounded)
},
expectedScore: 93, // Actual calculation: penalties total 7
expectedScore: 93, // Updated: 100-0-0-6-0-1 = 93
expectedGrade: "A",
expectError: false,
},
@@ -264,15 +264,15 @@ func TestAnalyzeSummary_CalculateHealthScore(t *testing.T) {
name: "grade C threshold",
summary: domain.AnalyzeSummary{
AverageComplexity: 15.0, // -12
CodeDuplication: 35.0, // -20 (new: >20% = High penalty)
CBOClasses: 10,
HighCouplingClasses: 4, // -10
CodeDuplication: 25.0, // -20 (>20% = High penalty)
CBOClasses: 20,
HighCouplingClasses: 2, // 10% ratio: -6 (new: Low penalty, 10% > 5%)
DeadCodeCount: 5,
CriticalDeadCode: 0, // No critical issues, so no dead code penalty
TotalFiles: 1,
},
expectedScore: 58, // Updated: 100-12-20-10 = 58
expectedGrade: "C", // 58 is in C range (55-69)
expectedScore: 62, // Updated: 100-12-20-6 = 62
expectedGrade: "C", // 62 is in C range (55-69)
expectError: false,
},
{
@@ -281,10 +281,10 @@ func TestAnalyzeSummary_CalculateHealthScore(t *testing.T) {
AverageComplexity: 22.0, // -20
CodeDuplication: 45.0, // -20
CBOClasses: 10,
HighCouplingClasses: 6, // -16
HighCouplingClasses: 6, // 60% ratio: -20 (new: High penalty)
},
expectedScore: 44,
expectedGrade: "D",
expectedScore: 40, // Updated: 100-20-20-20 = 40
expectedGrade: "D", // 40 is exact D threshold
expectError: false,
},
}

View File

@@ -24,8 +24,8 @@ type CBORequest struct {
ShowZeros bool // Include classes with CBO = 0
// CBO thresholds for risk assessment
LowThreshold int // Default: 5
MediumThreshold int // Default: 10
LowThreshold int // Default: 3 (industry standard)
MediumThreshold int // Default: 7 (industry standard)
// Configuration
ConfigPath string
@@ -169,8 +169,8 @@ func DefaultCBORequest() *CBORequest {
MaxCBO: 0, // No limit
SortBy: SortByCoupling, // Sort by CBO value
ShowZeros: false,
LowThreshold: 5,
MediumThreshold: 10,
LowThreshold: 3, // Industry standard: CBO <= 3 is low risk
MediumThreshold: 7, // Industry standard: 3 < CBO <= 7 is medium risk
Recursive: true,
IncludeBuiltins: false,
IncludeImports: true,

View File

@@ -45,8 +45,8 @@ type CBOOptions struct {
IncludeImports bool
PublicClassesOnly bool
ExcludePatterns []string
LowThreshold int // Default: 5
MediumThreshold int // Default: 10
LowThreshold int // Default: 3 (industry standard)
MediumThreshold int // Default: 7 (industry standard)
}
// DefaultCBOOptions returns default CBO analysis options
@@ -56,8 +56,8 @@ func DefaultCBOOptions() *CBOOptions {
IncludeImports: true,
PublicClassesOnly: false,
ExcludePatterns: []string{"test_*", "*_test", "__*__"},
LowThreshold: 5,
MediumThreshold: 10,
LowThreshold: 3, // Industry standard: CBO <= 3 is low risk
MediumThreshold: 7, // Industry standard: 3 < CBO <= 7 is medium risk
}
}

View File

@@ -24,8 +24,8 @@ func TestNewCBOAnalyzer(t *testing.T) {
IncludeBuiltins: false,
IncludeImports: true,
PublicClassesOnly: false,
LowThreshold: 5,
MediumThreshold: 10,
LowThreshold: 3, // Updated to industry standard
MediumThreshold: 7, // Updated to industry standard
},
},
{