> ## Documentation Index
> Fetch the complete documentation index at: https://mehen.ophi.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Code churn

> Lines added and removed over a file's history, absolute and normalized by file size.

**Code churn** measures how much a file has changed over its history. mehen ships both variants
from the literature:

* **Absolute churn** — lines added + lines removed, summed across every commit that touched the
  file. Matches code-maat's `abs-churn` and PyDriller's `(added + removed)` variant.
* **Relative churn** — absolute churn normalized by the file's size at the analyzed revision.
  Nagappan & Ball showed that *relative* churn predicts defect density well while *absolute*
  churn is a poor predictor: 500 churned lines mean something very different in a 100-line file
  than in a 10,000-line one.

## What mehen emits

| Key                      | Type  | Description                                                                               |
| ------------------------ | ----- | ----------------------------------------------------------------------------------------- |
| `history.churn.abs`      | int   | `Σ (added + removed)` across the file's walked history.                                   |
| `history.churn.relative` | float | `churn.abs ÷ max(size, 1)` where size is the file's code-line count at the same revision. |

The denominator is family-aware: source-code files use [`loc.sloc`](/metrics/code/sloc), SQL files
use `sql.loc.code`, Markdown files use `markdown.loc.tloc`. A file whose analyzer published no
line count falls back to a denominator of 1, keeping the value finite and deterministic.

`history.churn.relative` is one of the **default [`mehen diff`](/commands/diff) columns**.

## Semantics

* Churn follows the file across **renames** — a renamed file keeps its accumulated churn instead
  of resetting to zero.
* **Merge commits churn nothing** (their first-parent diff would double-count every line already
  attributed to the merged commits — the `git log --no-merges` convention).
* **Binary and oversized blobs churn zero lines**: anything failing a NUL sniff or larger than
  8 MiB is counted the way `git log --numstat` reports it (`-`), so a committed archive doesn't
  count its bytes as "source lines".

## How to read it

| Signal                                  | Interpretation                                                                                      |
| --------------------------------------- | --------------------------------------------------------------------------------------------------- |
| High relative churn, small file         | The file is being rewritten over and over — a stability problem or a design that keeps not fitting. |
| High absolute churn, low relative churn | A big file with proportionate change — usually fine.                                                |
| Rising churn in a diff                  | This change adds to an already-turbulent history; review accordingly.                               |

## References

* Nagappan, N. & Ball, T. (2005). *Use of Relative Code Churn Measures to Predict System Defect
  Density.* ICSE 2005. [DOI](https://dl.acm.org/doi/10.1145/1062455.1062514).
* [PyDriller process metrics](https://pydriller.readthedocs.io/en/latest/processmetrics.html).
* code-maat `abs-churn` ([repo](https://github.com/adamtornhill/code-maat)).

## See also

* [Commit frequency](/metrics/history/commit-frequency) — how *often*, not how *much*.
* [Hotspot](/metrics/history/hotspot) — change frequency × complexity.
