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
type ChurnOptions = {
threshold?: number;
windowDays?: number;
exclude?: string[];
};| Option | Default | Meaning |
|---|---|---|
threshold | 10 | Minimum number of changes within the window before a finding is produced |
windowDays | 90 | Rolling window in days |
exclude | [] | Glob patterns for paths to skip. Matched against the project-relative file path |
Example
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:
"src/payments/processor.ts" changed 14 times in the last 90 days — high churnFinding Identity
Findings are identified by file path:
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
excludeif the file category is noise for your project (generated files, lock files, etc.).
