返回资讯中心
外部精选
软件工程
#开发者实时流

开发者用 lint 钩子清理 AI 编码工具生成的冗余注释

来源标题:Linting, but this time it's personal

一名开发者针对 Agentic 编码工具到处乱写注释的问题,开始用钩子(hooks)自动清除无用注释,并逐步扩展为提升代码可读性的个人 lint 配置。他尝试在 AGENTS.md 中要求「不写注释」但效果有限,只能在审查 PR 时手动清理。文章还回顾了 1978 年诞生的 Lint 工具及现代 linter 的静态分析与自动修复能力。

Hacker News 最新dava962026/11/15 · 今天15 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

One thing that has really really annoyed me using Agentic coding tools is the models littering comments everywhere. They spread like the plague, one missed comment and before you know it there’s code in your commentbase.

I’ve had mixed experiences with trying to add some variation of “No comments” to AGENTS.md. They still leave them. Normally I’d catch them when I review my own PRs. But that’s wasting my own energy on a task that isn’t reviewing the changes.

This is where linting got personal for me. I started experimenting with hooks on a vendetta to remove useless comments. It has since grown into a more personal configuration to try and optimise my changes for readability.

The original Lint tool was developed in 1978, to perform static analysis on C source code. The idea behind it was to allow the developer to concentrate on the development of the program.

Modern linters can enforce coding conventions across a repository, catch potential bugs and automatically fix certain issues. They are often blazingly fast too. 🦀🔥

Write codefunc main() {var hi = "Hello world"fmt.Println("Hello, world")}Run lintererrorReview warningsdeclared and not used: hiFix issuesHello world

If you are anything like me you’d forget to run the linter during development. I would create my pull request only to find it cannot be merged because it errors in CI/CD.

Hooks are lifecycle events that either happen per session or per turn. I am using Claude Code hooks (other brands of hooks are available). The main events we are going to focus on here are:

The tools we are interested in here are the ones Claude uses to write with:

When Claude has finished using the Edit or Write tool, Claude Code fires the PostToolUse event. Our hook configuration has a PostToolUse entry with the matcher Edit|Write.

This can trigger a shell command, in this case it runs a linter dispatch script with the argument post-edit.

{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "node "$HOME/.claude/lint/dispatch.mjs" post-edit", "timeout": 120 } ] } ] } }

My first lint rule

I will use Go to demonstrate what I was trying to avoid:

package main

import "fmt"

func main() { // print a greeting printer("Hello World") }

// Takes a string parameter and prints it using fmt.Println func printer(s string) { fmt.Println(s) }

After reading enough LLM written code, I decided on a strict no inline comment policy. Out of all the inline comments I’ve seen models write almost all of them are redundant. They usually follow the same format, either narrating the code or explaining why something wasn’t done a certain way.

In my experience the Doc comments (DocBlock in PHP or TSDoc in TS) are usually more reasonable. But they also can grow into a mega comment depending on the function.

The primary languages I use day to day are: PHP, TypeScript and sometimes Go. I needed to create a solution that would work in each of the languages. These tools will only be run on files that are modified. They needed to run after every write, so they had to be fast.

All the numbers that follow are from a micro benchmark: one file of roughly 500 lines, full of linting errors, run five times per tool.

Go was the easiest one. I could just use the go/analysis package. Once Go’s build cache is warm, it takes 0.12s and 16 MB.

For PHP, I tried Mago, a Rust-based static analyser and linter, vs PHPStan, a PHP based static analyser. Both of these tools supported custom rules, which I needed. Unsurprisingly, Mago was much quicker: 0.10s vs 3.1s for PHPStan, or 1.5s with its result cache. On one file, Mago also used less memory: 104 MB vs 218 MB, or 108 MB for PHPStan with its cache.

That changes on an actual codebase. Mago reads everything into memory first, even for one file, so on our largest project it needs about 1.7 GB. PHPStan only loads each class it needs, so its memory grows gradually.

For TypeScript, I wanted to use Biome, but it didn’t support matching comments, which was my original use case.

I decided to do something experimental: I tried the official TypeScript 7.1 Beta compiler, written in Go, and tsrs, a Rust port of the TypeScript 7.1 checker.

I chose tsrs because it was the faster of the two and the most memory efficient. It type-checked the file in 0.15s with 37 MB, compared to 0.33s and 70 MB for the 7.1 Beta.

When a file is edited in a project, be it PHP, TS or Go. The dispatch.mjs script first checks the file extension to determine the language. Then it looks for go.mod, composer.json or tsconfig.json to find the root of the project.

This then runs the linting tool that I have chosen for the language on modified files.

In order for this to work correctly it needs to establish a baseline. This is a lint of the entire project in its pre-edited state. This is done on a per session basis. Any existing violations of my personal rules are recorded as part of the baseline because we only care about new changes.

Git is used to identify when a change happened outside of Claude’s session so like a merge or a pull.

When Claude calls the Edit or Write tool, Claude Code uses the PreToolUse Hook to run our dispatch script to capture the before state of the files.

Whenever Claude litters my changes with comments, it receives a fine:hook-output.json { "decision": "block", "reason": [ "Personal lint found 1 error(s) in your change to example.go. Fix them now:", "- example.go:6, [no-inline-comment] Inline comment: // print a greeting" ] }

This gives Claude the information it needs to remove the problematic line instantly and now, I never have to write another comment on a PR saying “remove this comment you £%^&”.

I’ve since expanded my personal rules to optimise the changes for readability and simplicity. Using a vibes-based combination of cyclomatic complexity, halstead complexity and cognitive complexity.

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →