Trusting the data
Everything on this site rests on one input: your git history. That history is dirtier than it looks, and the contamination matters more than the precision of any formula. This page is what goes wrong, and what dowse does about each case.
It is also the honest answer to "should I believe these numbers?". If your repository has problems dowse cannot correct for, you should not.
1. Tangled commits
The problem. One commit that does a refactor, a bug fix, and a rename at once. Every file in it now appears to "change together", fabricating coupling that does not exist.
What dowse does. Commits touching more than 50 files are excluded from the coupling calculation. They still count towards churn, because those files really did change — they just say nothing about coupling.
What is left to you. A tangled commit of 10 files is indistinguishable from a genuine one. No tool can fix this after the fact; smaller commits are the only real remedy.
2. Formatting, generated, and vendored commits
The problem. A Prettier run across the repository looks like an enormous change and inflates churn for every file it touched. Generated code — openapi-types.ts, protobuf output — changes constantly without anyone thinking about it.
What dowse does.
- Whitespace-only commits are detected with
git log -wand excluded. - Generated directories (
generated/,__generated__/), generated file patterns (*.gen.ts,*.generated.ts), lockfiles, minified files, andvendor/are out of scope by default. - With
--verify-churn, the AST of each revision is compared, separating renames and reformatting that a line diff cannot tell apart from real edits.
This one was found by dogfooding
On a real repository, a generated openapi-types.ts ranked as the number one hotspot before these exclusions existed. Pure noise, with a very high score.
3. Renames and moves
The problem. Rename a file and its history restarts from zero. In a monorepo, where directory reorganisation is routine, this is the single most damaging effect.
What dowse does. Rename and copy detection is enabled when reading the log, so moves are followed. With --verify-churn, a file whose content is unchanged after a move is classified identical and does not count as churn.
4. Package boundaries
The problem. In a monorepo, a change to packages/ui is not the same kind of event as a change to apps/web. Treating the repository as one flat pile of files mixes them together.
What dowse does. The workspace configuration is read first and every file is attributed to a package. Metrics are computed per file and aggregated by LoC-weighted mean. Coupling is computed at both file and package granularity.
5. Squash merges and rebases
The problem. With squash merges, a branch's 30 commits become one. Change frequency drops and the contributor count is understated.
What dowse does. Nothing automatic — the information has been destroyed. What it does do is report the commit count it worked from, so you can see that a repository with 200 commits a year and 40 developers is squash-merging.
What is left to you. Compare across time windows within one repository rather than across repositories with different merge policies.
6. Developer identity
The problem. The same person commits as alice@corp.com, alice@personal.com, and Alice. They are counted as three people, ownership is diluted, and the bus factor is overestimated — the package looks safer than it is.
What dowse does. Author names and emails are read via git log %aN/%aE, which applies .mailmap.
What is left to you. If you have no .mailmap, add one. This failure is silent: the numbers look fine, they are just wrong in the optimistic direction.
7. Bot commits
The problem. dependabot, renovate, and CI bots produce large volumes of commits and distort both churn and ownership.
What dowse does. Commits from authors with a [bot] suffix, known bot names, or noreply addresses are removed before churn and ownership are computed. The number removed is printed.
8. Shallow clones
The problem. fetch-depth: 1 — the CI default — leaves almost no history. Churn, coupling, and age all become meaningless.
What dowse does. It detects a shallow clone at startup, warns clearly, and records shallow: true in the output.
The most common way to get wrong numbers
It fails quietly: you get a full-looking report built on three commits. In CI, always set fetch-depth: 0.
9. Raw NUL bytes in source
The problem. If a source file contains a literal NUL byte, git treats it as binary and --numstat reports - instead of line counts. That file's churn silently disappears.
What dowse does. It parses - as unknown rather than zero, so the file is not quietly counted as unchanged.
What is left to you. Write \0 as an escape rather than embedding the byte. dowse's own codebase had this in three files.
The principle underneath all of this
Every exclusion above is reported in the output.
excluded: 312 merge, 47 bot, 18 format-only, 0 ignore-revs (51 large changesets flagged)An analysis that quietly drops data invites you to read the result as "everything, considered". A number whose scope you cannot see is not a number you can act on — so dowse always shows its scope, including when the answer is unflattering.