程式碼註解是寫給另一類讀者看的
A comment is for a different reader
Matt Cockayne 主張,程式碼註解應解釋原因而非操作方式;工具使用者需要的操作、限制及故障處理則應寫入文件。他曾為一款工具撰寫約 90 頁文件,將文件嵌入二進制檔,並加入只根據文件內容回答問題的 AI 命令,讓用戶可查閲與所用版本相符的資料。他亦指出,文件能令工具方便使用,卻不能代替把維護工作交予其他人,培養第二位維護者。
When did you last read the source of a tool to find out how to use it?
I'll wait. I've been asking people that for a few weeks now and the answer, once they've stopped laughing, lands somewhere between "never" and "only when the docs lied to me". So it's a bit odd, perhaps, that "the code has no comments" is still the first thing anyone reaches for when they want to say a codebase is a mess.
Notes on the engine, a manual in the glovebox
The tool I wrote about last month had comments on just under ten per cent of its lines, and if anything I'd have argued for fewer. It also shipped with about ninety pages of documentation, published as a site and baked into the binary behind a docs command so you could read the lot offline on a train, which people did.
Those two facts look like a contradiction, and I'd argue they were one decision (I wish I'd said so at the time, rather than leaving people to work it out for themselves).
Any piece of code that matters has three readers, and just one is the person people picture when they say "reader". There's the engineer using the tool... at that job there were hundreds of them, most of whom had never written a line of Go and had no wish to start, thank you very much. Then there's whoever is changing it, and there were a handful of those on a good day. And then there's the poor soul reading the diff in six months, trying to work out why on earth it does that, and more often than not that's you, having forgotten, swearing at whoever wrote it before realising who that was.
A comment is really for the second and third of those. It's a note scratched onto the engine block for the next mechanic. The driver very rarely lifts the bonnet, and you wouldn't hand them the wiring diagram and call it a manual; they get the book in the glovebox, and the book says how to drive it and what the warning lights mean and what not to do, which is a completely different document written for a completely different person. The notes on the engine being terse doesn't tell you much at all about whether the glovebox is empty.
So "no comments, therefore tribal knowledge" is making two assumptions at once, and for most tools I suspect they're both wrong. It assumes the code is the only place knowledge can live, and it assumes everyone who needs the knowledge is a mechanic.
What a comment is actually for
Here's my rule. I've held it long enough that I've just this month written it into the instructions my AI agents work from, because they didn't have it and had taken to writing small essays above functions (they do love an essay).
A comment says why. It doesn't say how. If the how needs a paragraph then the code needs refactoring, not annotating, and a comment explaining what a function does is really a confession that the function's name doesn't. Well-written code carries its meaning in its symbols, and that's why I think a comment restating the code is worse than no comment at all: the reader has to read it, work out that the name already said it, and throw it away, on every single visit... death by a thousand paper cuts.
I'll make one up, though hand on heart I have reviewed this exact comment more times than I care to count:
// shoppingCartExceedsFreeShippingBoundary confirms that the cart is eligible for
// free shipping. Why? Because we offer free shipping when the customer spends
// more than £50.
func shoppingCartExceedsFreeShippingBoundary(cart Cart) bool {
return cart.Total() >= 50
}
Hardly a word of that comment survives the function's name and its one-line body. Delete it and you've lost next to nothing. A comment that would earn its line on that function carries something the code can't say. That the fifty is set by Marketing and changes each January, say, or that it's >= rather than > because of a row with Finance in 2019 that no one is keen to reopen. That's a why, that's worth a line, and then you get out of the way and let the code speak.
A rule like that has an exception, and I didn't honour it. Go renders the comment above every exported symbol straight into the package documentation, so an export with no doc comment is a hole in the public surface however good the name is, and on that codebase barely a quarter of the exports had one. A fair cop! If I could go back and write one piece of documentation for that tool, it would be those skipped one-liners on the functions the next mechanic was going to need.
What ninety pages were for
The documentation was sized by the audience, not by my enthusiasm for writing it (which, for the record, is limited).
Hundreds of engineers across a lot of teams, who needed to log in to a cluster, or launch a tools pod, or push an upgrade through eighty-odd Terraform modules before lunch, and who'd have been actively harmed by being pointed at the Go that did it. Plenty of them could have read it. It just would have told them how it worked rather than what they needed, which was how to use it, what it would refuse to do and why, and what to try when it went wrong. Why the tools pod is only permitted in one cluster per environment is a paragraph of reasoning about auditability and who can reach what, and that paragraph lives in the docs. In the code it's a single check, and it should be.
Two things stopped those pages rotting the way documentation usually does. They were embedded in the binary, so the docs you had were the docs for the version you were running, which is the only version of "keep the docs in sync" that has ever actually worked for me. And I bolted an AI onto them, a docs ask command that answered questions from that corpus and nothing else, and the code never went anywhere near the model. What it knew was exactly, and only, what a user was allowed to know. It turned ninety pages that hardly anyone was going to read end to end into something people actually asked, and that turned the docs into a thing worth maintaining, because a stale page now gave a wrong answer out loud, in front of someone, rather than sitting on a wiki being wrong in private.
What was tribal
I said the criticism about doc comments was fair. This one's fairer, and I'd rather make it myself than have it made for me.
Most of the commits on that tool have my name on them. It launched without fanfare and grew a command at a time, and the onboarding was me sitting down with a team and walking them through it, over and over, team after team. From the outside a tool with one main author and a lovely manual is indistinguishable from tribal knowledge with a lovely manual, and in the early days it was tribal knowledge, not for want of trying, though wanting doesn't count. The docs were my attempt to make it not so, and they did their job for the hundreds of drivers, but what a manual can't do is manufacture a second mechanic. Only handing someone the spanner and standing well back does that, and I did, and it took most of a year, and I still walked out with most of the commits being mine.
That's the limit of documentation and I don't think you can get round it, however many pages you write... a manual makes a tool usable by people who'll never maintain it, and does precious little about who does.
The glovebox
You'll have noticed I haven't shown you a line of that tool. I can't, it isn't mine to show.
But you know what it was for, and why the pod was only ever allowed in one cluster, and what I'd do differently, and where that fifty-pound threshold came from... which is all any of its drivers ever had, and it got them where they were going.
Originally published at phpboyscout.uk on 28 October 2026.
來源:DEV Community · dev.to