Skip to content

churn

Needs

git_commits · git_file_changes — 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.

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

Example

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

export default defineConfig({
	collectors: [
		['@maat-tools/collector-git', { since: '90 days ago' }],
	],
	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.

Handling a New Finding

When a file crosses the threshold and you are not ready to refactor:

  • Baseline — acknowledge it and set an expiry date to revisit.
  • Axiom — declare the file's churn as permanently accepted (e.g. a config file that changes by design).
  • Exclude — add the path to exclude if the file category is noise for your project (generated files, lock files, etc.).