Add median metrics to aggregate analysis#18
Conversation
Add MedianFunctionLength and MedianComplexity to AggregateMetrics, complementing the existing averages and P95 percentiles. Medians are more robust to outliers than averages for skewed distributions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| }) | ||
|
|
||
| metrics.MedianComplexity = calculateMedian(allFunctions, func(fn *parser.FunctionMetrics) int { | ||
| return fn.Lines |
There was a problem hiding this comment.
MedianComplexity is computed from fn.Lines instead of fn.Complexity — this looks like a copy-paste from the MedianFunctionLength block directly above. As written, median_complexity will always be identical to median_function_length and never reflects actual cyclomatic complexity.
code-review-assistant/internal/analyzer/aggregator.go
Lines 72 to 74 in 8260099
| return fn.Lines | |
| return fn.Complexity |
| values := make([]int, len(functions)) | ||
| for i, fn := range functions { | ||
| values[i] = getValue(fn) | ||
| } | ||
|
|
||
| mid := len(values) / 2 | ||
| if len(values)%2 == 0 { | ||
| return (values[mid-1] + values[mid]) / 2 | ||
| } | ||
| return values[mid] |
There was a problem hiding this comment.
calculateMedian indexes the middle element(s) of values without sorting first, so it returns an arbitrary element from the original traversal order rather than the true median. The sibling calculateP95 below (which also uses sort already imported in this file) calls sort.Ints(values) before indexing — this function is missing the equivalent call. The new tests don't catch it because their fixtures happen to already be in ascending order.
code-review-assistant/internal/analyzer/aggregator.go
Lines 79 to 90 in 8260099
| values := make([]int, len(functions)) | |
| for i, fn := range functions { | |
| values[i] = getValue(fn) | |
| } | |
| mid := len(values) / 2 | |
| if len(values)%2 == 0 { | |
| return (values[mid-1] + values[mid]) / 2 | |
| } | |
| return values[mid] | |
| values := make([]int, len(functions)) | |
| for i, fn := range functions { | |
| values[i] = getValue(fn) | |
| } | |
| sort.Ints(values) | |
| mid := len(values) / 2 | |
| if len(values)%2 == 0 { | |
| return (values[mid-1] + values[mid]) / 2 | |
| } | |
| return values[mid] |
|
Test PR for the review workflow — both planted bugs were correctly flagged with inline comments. Closing without merging. |
Summary
Adds
MedianFunctionLengthandMedianComplexitytoAggregateMetrics, complementing the existing averages and P95 percentiles. Medians are more robust to outliers than averages for skewed distributions, giving a better picture of the typical function in a codebase.calculateMedians/calculateMedianhelpers in the aggregatormedian_function_length/median_complexity🤖 Generated with Claude Code