Вам теж набридли коментарі від 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 без testdata | 11.7% |
| testify | 24.0% |
| yaml/v3 | 28.1% |
| Go stdlib: strings | 28.9% |
| Go stdlib: encoding/json | 29.0% |
| Go stdlib: net/http | 32.3% |
| Go stdlib: sync | 41.3% |
| golangci-lint з testdata | 109.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++, бо саме їх я пишу. Якщо вам потрібна інша мова, пишіть у коментарях, яку саме.
Коментарі та запитання · Google / GitHub / анонімно