Skip to content

churn

Needs

gitCommits · gitFileChanges — from @maat-tools/collector-git

churn detects files that change too frequently within a rolling time window. A file that accumulates changes faster than the rest of the codebase is a signal that it is doing too much, that too many concerns depend on it, or both.

What It Checks

The rule joins git commits with their file changes, filters to the configured window, counts changes per file, and reports a finding for any file that meets or exceeds the threshold.

Deleted files are counted the same as modifications — a file deleted and recreated frequently is still a churn signal.

What this rule detects

A file that accumulates commits faster than the rest of the codebase, as seen in git history over a rolling time window.

# Commits touching src/payments/processor.ts in the last 90 days

a1b2c3d  fix: handle payment timeout
d4e5f6g  feat: add retry with exponential backoff
7h8i9j0  fix: race condition under concurrent load
k1l2m3n  refactor: extract validation into helper
o4p5q6r  fix: memory leak in long-running jobs
s7t8u9v  chore: align error messages with API spec
w0x1y2z  fix: currency rounding edge case
...
# 14 commits total → exceeds threshold of 10

Finding:

txt
"src/payments/processor.ts" changed 14 times in the last 90 days — high churn

Options

ts
type ChurnOptions = {
	threshold?: number;
	windowDays?: number;
	exclude?: string[];
};
OptionDefaultMeaning
threshold10Minimum number of changes within the window before a finding is produced
windowDays90Rolling window in days
exclude[]Glob patterns for paths to skip. Matched against the project-relative file path

Configuration

ts
import churn from '@maat-tools/git-rules/churn';

export default defineConfig({
	collectors: [
		['@maat-tools/collector-git', { sinceDays: 90 }],
	],
	rules: [
		churn({
			threshold: 10,
			windowDays: 90,
			exclude: [
				'**/index.ts',
				'**/*.test.ts',
				'**/*.json',
			],
		}),
	],
});

If src/payments/processor.ts changed 14 times in the last 90 days, the rule reports:

txt
"src/payments/processor.ts" changed 14 times in the last 90 days — high churn

Finding Identity

Findings are identified by file path:

ts
ruleIdentifier: { path }

A finding for a given file remains stable across runs as long as the file keeps exceeding the threshold. It resolves automatically as commits age out of the window.

Auto-Resolution

Unlike structural findings, churn findings are self-healing. Once a file stops accumulating changes, old commits slide out of the window and the finding disappears without any manual action.