DEV Community

Revin
Revin

Posted on Originally published at revin.com.br

I ran git blame on the files that touch money: 87% of the commission logic had one author

Every planning meeting at a client I was helping ended the same way. Someone would pull a card about the commission rule, and someone else would say "better wait for Marina". Nobody found that odd. She always sorted it out.

I wanted a number instead of a feeling, because "only Marina touches this" does not survive a budget conversation and a percentage does. So I spent an afternoon on the repository. Node, Postgres, about 64k lines, six years old, four developers on payroll and a fifth who left in 2023.

The method is crude and it fits in one afternoon. Write down the rules that move money, map each one to the files that implement it, and ask git who owns those lines.

Step 1: list the rules, then the paths

This part is not automatable and that is fine. I sat with the product person and we listed ten rules that touch money: how the discount is calculated, when an order can be cancelled, what releases a partner payout, how tax is split on an invoice, and so on. Then a developer mapped each rule to the files that actually implement it.

$ cat money-paths.txt
src/billing/commission.ts
src/billing/discount.ts
src/orders/cancellation.ts
src/payouts/partner-release.ts
src/invoices/tax-split.ts
...
Enter fullscreen mode Exit fullscreen mode

Ten rules turned into 14 files. The mapping took longer than the script.

Step 2: ask blame who wrote the surviving lines

while read -r f; do
  total=$(git blame -w -M -C --line-porcelain "$f" | grep -c '^author ')
  top=$(git blame -w -M -C --line-porcelain "$f" \
        | grep '^author ' | sort | uniq -c | sort -rn | head -1)
  printf '%-34s %4s lines %s\n' "$f" "$total" "$top"
done < money-paths.txt
Enter fullscreen mode Exit fullscreen mode

Output, names changed:

src/billing/commission.ts         412 lines  358 author Marina
src/billing/discount.ts           287 lines  169 author Marina
src/orders/cancellation.ts        233 lines  201 author Marina
src/payouts/partner-release.ts    191 lines   96 author Rafael
src/invoices/tax-split.ts         164 lines  102 author Marina
Enter fullscreen mode Exit fullscreen mode

The commission file came back at 87% single author. Four of the ten rules had the same name above 80%. That is the whole finding, and it took an afternoon.

The flags matter more than the loop. -w ignores whitespace changes, -M follows lines moved inside a file, -C follows lines copied from other files. Without them the number is a lie, and I will get to how badly.

Step 3: check who guards the door

Authorship is half the story. The other half is review, because a rule with one author and three habitual reviewers is much less concentrated than the blame output suggests.

gh pr list --state merged --limit 300 \
  --search "billing/commission in:path" \
  --json number,reviews \
| jq -r '.[].reviews[].author.login' \
| sort | uniq -c | sort -rn
Enter fullscreen mode Exit fullscreen mode
  19 marina
   3 rafael
   1 caio
Enter fullscreen mode Exit fullscreen mode

Nineteen of the 23 merged pull requests touching that path were approved by the same person who wrote it. So the review was not spreading the knowledge, it was confirming it.

What did not work

Three attempts died before the one above.

First I ran git shortlog -sn -- <file>, counting commits per author. It ranked a developer first on the discount file because he had done a dependency bump that reformatted imports. Commit count measures traffic, not ownership.

Then I ran blame without -w -M -C. A Prettier rollout in 2023 rewrote almost every line in the repository, and the output cheerfully told me that one engineer owned 71% of the entire business logic. He had joined four months earlier. Any blame based metric that ignores whitespace commits will hand you that kind of nonsense with a straight face.

Last, I tried correlating with cyclomatic complexity, because that is what most answers on Stack Exchange recommend when someone asks how to quantify technical debt. The most complex file in the repository had four authors and nobody was afraid of it. The file that froze the planning meeting was 233 lines of plain conditionals. Complexity told me where the code was ugly. It said nothing about where the company was exposed.

Why the ugly code is not the debt

In systems that land on my desk after the previous vendor walked away, the thing that eats the first month is rarely the chained slow query or the test suite without a single assertion. It is figuring out why a strange conditional exists in the order service. That conditional is a commercial agreement somebody closed on the phone in 2019.

The code was the only living record of the rule. Living records read slowly, and the person who could read it fast changed jobs.

Construction has a name for the missing artifact. There is the design, drawn before anything is poured, and the as built, the drawing of how the structure ended up after every decision made on site, including the beam that moved because the soil did not cooperate. Skip the as built and you find the deviation by opening a wall with the building already occupied. In software the wall is invisible and the person who knew where it was is somewhere else.

There is a cheap test for whether your docs are as built or decoration. Give a small change on that rule to someone who has never touched it, with the document and nothing else. If she opens Slack in twenty minutes, you have a file rather than a document.

What actually moved the number

A documentation sprint fixes little and rots fast. Rotation moves the blame output, in three habits:

  • Every task on a concentrated rule ships as a pair, with the person who does not know it on the keyboard and the person who does only answering questions.
  • On call rotates through everyone, with the author of the rule in second line rather than first, because on call is the only moment nobody can postpone the question.
  • The rule owner writes half a page of why, including the options that were thrown out, and the page counts as done only after someone else changed the rule reading nothing but it.

This is slower. A pair delivers something like 20% to 30% slower in the first weeks and the team will say so out loud. It is the price of pulling one name off four lines of that list.

Where the advice is bad: if you are three people still hunting for customers, concentration is an advantage. Rotation eats the week and your real problem is whether anyone pays. The math flips when revenue starts depending on that system, and it flips without warning.

I re-ran the script on those paths eleven weeks later. Commission went from 87% to 61%, which is not great and is a lot better than a wiki nobody opened.

How do you measure this where you work? I have seen bus factor plugins, CODEOWNERS coverage reports and pure gut feeling in planning, and I am honestly not sure the plugins beat the afternoon with blame. If you have a metric that survived contact with a real repository, I want to read it.

Originally published on the Revin blog: https://revin.com.br/en/blog/cognitive-debt-technical-debt-outside-code

Top comments (0)