Вам теж набридли коментарі від AI в коді? Тоді commentdiet іде до вес

AI-агенти коментують кожен рядок: «перевіряємо чи порожній», «повертаємо помилку». Написати в CLAUDE.md «не коментуй очевидне» допомагає на пів години. Тому я зробив маленький лінтер: він міряє співвідношення символів коментарів до символів коду у файлі, директорії та проєкті і валить pre-commit, якщо ліміт перевищено. Go і C/C++, один docker run, образ трохи більше мегабайта.

Пролема виглядає так. Даєш агенту задачу, він її робить, і робить непогано. А потім відкриваєш diff:

// processItems processes the items in the slice.
// It iterates over each item and processes it.
func processItems(items []Item) error {
	// Check if items is empty
	if len(items) == 0 {
		// Return nil if there are no items to process
		return nil
	}
	// Iterate over all items
	for _, item := range items {
		// Process the current item
		if err := process(item); err != nil {
			// Return the error if processing failed
			return err
		}
	}
	// Return nil on success
	return nil
}

У цьому шматку 221 символ коментарів на 146 символів коду. 151%. Жоден коментар не каже нічого, чого не каже сам код.

Пишеш у CLAUDE.md: «Не коментуй очевидне. Коментар лише там, де код не може сказати сам». Працює. Пів години. Потім контекст стискається, або задача довга, або просто настрій такий, і воно знов. Людина-рев'ювер від цього втомлюється і починає пропускати. Лінтер не втомлюється.

Що міряє

Одне число: символи коментарів поділити на символи коду, пробіли не рахуються. Символи, а не рядки, бо один рядок на 120 символів пояснень і один рядок // nolint це дуже різні речі, а по рядках вони однакові.

Число міряється на трьох рівнях, і в кожного свій ліміт:

Файли, де коду менше за min_code_chars (200 за замовчуванням), не перевіряються окремо, але їхні символи йдуть у суми. Інакше файл із трьох рядків і одним коментарем валив би все.

Не рахується як коментар: ліцензійні шапки, cgo-преамбули, директиви на кшталт //go:build чи //go:generate. Згенеровані файли (з DO NOT EDIT) пропускаються повністю, разом із .git.

Скільки це в реальному коді

Щоб зрозуміти, які ліміти ставити, я проміряв те, що було під рукою:

ЩоКоментарі / код
commentdiet (сам)8.2%
Цей блог (Go)10.3%
Невеликий сервіс на Go з цього ж сайту13.2%
golangci-lint без testdata11.7%
testify24.0%
yaml/v328.1%
Go stdlib: strings28.9%
Go stdlib: encoding/json29.0%
Go stdlib: net/http32.3%
Go stdlib: sync41.3%
golangci-lint з testdata109.6%

Картина проста. Прикладний код живе на 8–13%. Бібліотеки, де кожен експортований символ має doc-коментар, на 25–30%. sync це 41%, але там кожен коментар пояснює memory model, і хай пояснює. А 110% у golangci-lint це один файл у testdata з 845 кілобайтами коментарів для тесту на довгі рядки. Саме для таких випадків є exclude і overrides.

Потім я прогнав його з лімітами за замовчуванням по всьому, до чого маю доступ: 24 робочі репозиторії на C++ і Go та 10 моїх власних, з цього сайту.

34 репозиторії, ліміти за замовчуванням
Пройшли з першого разу2
Порушень на репозиторійвід 2 до 917
Власний код на рівні проєкту вище 20%6
Власний код: хоч один файл вище 25%28
Власний код: хоч один файл, де коментарів більше за код16
Ліміт на файл, щоб пройти разом із contrib/ і build/до 3027%

Тут три висновки. Перший: у середньому все добре. Власний код на рівні проєкту майже всюди вкладається у 20%, тобто розробники загалом не зловживають. Другий: середнє нічого не значить. У 28 репозиторіях із 34 є файл вище 25%, у 16 є файл, де коментарів більше, ніж коду. Саме ці файли лінтер і називає, і саме заради них він існує. Третій: без exclude на contrib/, build/, vendor/ цифри абсурдні. Щоб пройти з чужим кодом усередині, ліміт на файл довелося б ставити 285%, а в одному випадку 3027%.

Тож на існуючому репозиторії перший запуск майже гарантовано впаде. Це не баг, це і є звіт. Порядок дій: виключити чуже, подивитися на названі файли, для законних винятків на кшталт тестів із великими фікстурами зробити overrides.

Ліміти за замовчуванням

Файл 25%, директорія 20%, проєкт 15%. На рівні проєкту це м'яко: прикладний код проходить з запасом, stdlib не пройде, агент зі своїми 150% тим більше. На рівні файлу це жорстко, і так задумано. Якщо не подобається, кладете .commentdiet.yaml у корінь:

file_max_ratio: 0.25
dir_max_ratio: 0.20
project_max_ratio: 0.15
min_code_chars: 200
exclude:
  - "vendor/**"
overrides:
  - path: "**/testdata/**"
    disable: true
  - path: "**/*_test.go"
    file_max_ratio: 0.50

Будь-який ліміт можна вимкнути через null. Патерни глобів відносно файлу конфігурації, ** це будь-яка кількість сегментів. Невідомий ключ у конфігу це помилка, а не мовчазне ігнорування.

Як запустити

З кореня проєкту:

docker run --rm -v "$PWD:/src:ro" serguacom/commentdiet:latest

Образ це scratch з одним статичним бінарником, трохи більше мегабайта, є під amd64 і arm64. Проєкт монтується read-only, нічого не пише і нікуди не ходить. Exit code 0 — чисто, 1 — забагато коментарів, 2 — помилка конфігу або запуску.

Pre-commit hook, у .git/hooks/pre-commit:

#!/bin/sh
docker run --rm -v "$(git rev-parse --show-toplevel):/src:ro" serguacom/commentdiet:latest || exit $?

Або без Docker:

go install github.com/serguacom/commentdiet@latest

Вивід у форматі, який розуміє будь-який редактор і CI:

verbose.go:1:1: comment ratio 45.3% exceeds file limit 25.0% (comments 136, code 300 chars)
lib: comment ratio 23.1% exceeds dir limit 20.0% (comments 231, code 1000 chars)
project: comment ratio 17.0% exceeds project limit 15.0% (comments 340, code 2000 chars)

Чого він не робить

Не оцінює якість коментаря. Він не знає, що // Return nil on success це сміття, а // WalkDir lstats its root: a symlinked cwd would be skipped whole це найкорисніший рядок у файлі. Він знає лише, що корисні коментарі короткі, а сміття довге і його багато. На практиці цього достатньо: агент, якому не дають написати сто коментарів, пише п'ять, і ці п'ять раптом починають щось означати.

Це не заміна правилу в CLAUDE.md. Правило каже агенту, що робити. Лінтер каже, що буде, якщо не зробити.

Dogfooding

У самого commentdiet ліміти 18% на файл, 14% на директорію, 8% на проєкт, і він на 8.2%. Тобто на межі, і кожен новий коментар треба чимось виправдати. Мені так подобається.

Код: github.com/serguacom/commentdiet, ліцензія BSD-2-Clause. Образ: serguacom/commentdiet. Поки що лише Go і C/C++, бо саме їх я пишу. Якщо вам потрібна інша мова, пишіть у коментарях, яку саме.

Оцініть пост
0.0 (0)

Коментарі та запитання · Google / GitHub / анонімно