我是如何把自己的书过度工程化的
文章摘要
Ben Balter(前 GitHub 员工)在 2026 年 8 月 17 日发表了这篇约 38 分钟阅读量的长文,记录他如何用软件工程的整套基础设施写出并自出版了一本名为《Open and Async》的书。他自己给的 TL;DR 是:用 Markdown 和 Git 写书,搭了一条 CI 流水线,把同一份源码变成五种格式,并运行数千项自动检查——从文风 linter 到一个抓出他在五个不同章节里重复同一论点的 LLM。AI 审阅了每一页,但每一个字都是他写的。开篇的自嘲很到家:「我的书有一个 linter,会因为我把 open source 写成连字符形式而对我大喊大叫。它运行一整套自动检查,每次 git push 都重建所有格式,而且只要我稍微暗示自己还在一份已经离开的工作岗位上,构建就会失败。为了一本书。一个人写的书。」
内容层。 每一章是一个独立的 Markdown 文件,一个 index.yml 定义章节顺序,便于重排或新增章节。编辑器是 VS Code 加若干文风扩展。一个值得注意的实践细节:他大部分内容是在 iPad 上写的——浏览器标签里的 Codespaces 加一个蓝牙键盘,经常是在离开办公桌的夜晚和周末,Git 仓库保证不管在哪里打开都同步;「一个坏主意距离消失只有一个 git revert」。
测试层。 本地实时运行的 VS Code 扩展有六个:Markdownlint(Markdown 语法与格式一致性)、Harper(语法与措辞,完全在设备上运行)、LanguageTool(语法、标点、文风)、Vale(他自己的家规与禁用词)、Alex(不敏感或排他性措辞)、Write-good(弱文笔:被动语态、模糊词、陈词滥调)。这六个在 CI 里也全部运行(Alex 和 Write-good 在 CI 里被折进 Vale)。合起来是「数百条精选文风规则叠加在一个完整语法引擎之上」。
CI 里还有他自己写的测试套件:一个独立的 Node 内容校验脚本、一个 Vitest 套件、以及 Playwright 规格。校验器是最有意思的部分,每个只有几行,读取 Markdown 并推出带文件与行号指针的错误。他最喜欢的那个:他已经不在 GitHub 工作了,所以任何声称他还在的句子都会让构建失败(用正则匹配「is/are … at GitHub」和「works/leads/runs/manages/directs at/for GitHub」两类现在时雇佣声明)。这个校验器的存在源于他犯过一次这个错,而这正是整套模式:第一次错误逃过他的眼睛时,他不只修那一句,而是写一条规则,「这样我就永远不必再靠肉眼去抓它」。他把这叫做「文稿的牛群而非宠物」(cattle, not pets for prose)——不要手工照护每一章,用策略治理整个牛群。这类校验器约有三十个,他点名的其他几个包括:validateOpenSourceHyphenation(open source 是名词不是动词,且永不加连字符)、validateHypotheticalHooks(段首出现公式化的 AI 味开场,如「Picture this…」「Imagine…」「Consider a…」)、validateSentenceStarters(连续三句以上用同一个词开头)、validateNoBareUrlLinkText(链接可见文字不能只是原始 URL)、validateCalloutBalance(书同时面向管理者和一线贡献者,所以一个「For managers」提示框附近必须有对应的「For ICs」)、validateCrossReferences(每个锚点交叉引用都必须指向真实存在的标题),另外还有两打左右针对小型大写、破折号、重复词、连接号范围、TL;DR 长度等等的规则。
有一条校验器抓到了他本人:validateHypotheticalHooks 标出了一段他确信是自己写的段落开头——而它确实是他写的。「但冷读一遍,它真的听起来像代笔的;我从读太多生成文本中吸收了那种韵律,产出了一份对虚无的流畅模仿。」他的结论是:linter 分不出好文笔和坏文笔,但它能标出你停止思考时会伸手去抓的那些模式,而这恰恰是你在自己的草稿里看不见的东西。CI 套件的规模是:约 70 个测试文件、2204 个测试用例、约 3900 个 expect() 断言,再加上上述校验器带来的约 1600 项逐章结构检查。
审计层。 linter 抓错误,但无法告诉他书是否在自我重复、某一章是否写得好。为此他建了三层重复检测:jscpd(token 级复制粘贴检测,能抓到较长的逐字块);n-gram(把 index.yml 里每一章分词、剥掉 Markdown/Pandoc 语法、构建词 n-gram,标出出现在多于一章的短语,并给出「最常重复的套话」排名;有跨章和章内两种模式);semantic(前两者做不到的那件事——同一个观点用不同的词重说一遍)。语义层是按需触发的 LLM 审计,设计上从不把整本书喂给模型,而是三次小单元遍历:intra(每章一次调用,「这一章在哪里重述了自己?」)、cross(对所有章节的 TL;DR 做一次调用,产出概念重叠章节的地图——「一次调用的代价换来全书扫描」)、arguments(逐章一次调用抽取核心主张,然后用最后一次调用把跨章的相同论点聚类)。
结果是:两个机械层最终跑干净了,但那是因为它们已经先抓出了复制粘贴和回收的措辞并被修掉了。语义层抓到的是更微妙的那类——已经没有逐字重复,只是同一个观点换了词说。它抓到他在五个不同章节里做了同一个论点:「把办公室习惯搬到线上,不等于远程优先地工作」;此外还有散布在 51 个章节中的 166 处更小的自我重述。「我把自己转述得太好了,便宜的手段抓不住我。」
内容审计还分概率性(LLM 读章节并给意见,跑两次结论可能变)与确定性(规则与算术,同一输入同一输出)两类。概率性审计里最有价值的是 Claims(主张)这一「镜头」:它在一句关于 GitHub 每月全员大会「大约把每场一半时间用于现场问答」的话上停住了——这个数字他本来确信无疑,但其实是错的,实际更接近三分之一,而且这个比例多年来一直在变。「没有 linter 会标出这个:这是干净、自信而恰好为假的散文。只有一个真正在问『这是真的吗?』的读者才能抓住它,否则我就会把它发出去。」Claims 是「文稿审计」测试里 20 个单一用途镜头之一,每个镜头只问一个狭窄的问题,好让模型无法用模糊的「看起来不错」蒙过去;--lens=all 跑所有镜头,--models / --rounds 增加去重后的多模型与自一致性小组,让一个发现必须在多于一个模型(或多于一次运行)中存活才算成立。文中列出的其他镜头包括:AI-tells(读起来像机器生成而非他的声音的措辞)、Hook(开场是否在做实事还是清嗓子)、Legal(法律或声誉风险,如点名、未经核实的主张)、Dated(会老得难看的时效性表述,如「最近」「今年」)、Global(会让非美国读者绊倒的习语和文化假设)、Dual-audience(一章是否同时服务管理者和一线贡献者)、Promise(一章是否兑现了全书的核心承诺)。此外还有一个特质分析测试,为每章的说服力与吸引力特质打分,抓出技术上干净但平淡的段落以便他再做一次人工修订。确定性审计包括章节与段落长度分布、全书阅读时长、一个硬性的 EPUB 体积预算(Kindle 会对超大文件的投递做惩罚)、全书一致性检查器,以及一个统计仪表盘,其 --check 模式要求每一项「注意事项」(缺 TL;DR 的章节、不平衡的提示框)都为零,否则构建失败。仪表盘上《Open and Async》的数字是:总词数 99651,阅读时长 399 分钟,72 章,平均每章 1384 词,103 个管理者提示框、102 个 IC 提示框、123 条 pro tip、62 个「异议」(objections)。
设计层。 他坦承自己远不是设计师,也从未出版过电子书。两个「顿悟」改变了他对出版的理解:「电子书就是穿着风衣的网站」——电子书只是 HTML 和 CSS,只不过是极度精简的版本,能做网站就能做电子书;以及只要肯下功夫,印刷书也可以是网站——CSS 原生就有强大的 print 媒体查询和分页规则,包括左右页样式、标题页、页码等等。他用 Tailwind CSS 做内文设计(不是因为它为书而生,而是因为这是他每天在用的东西),Typography 插件提供了排版基线。封面则特意雇了人类设计师:「对于一本讲真诚的书,第一印象必须是真诚的。」
有一个「看不见的泄漏」故事很典型。为了把电子阅读器的调整隔离在平装本之外,他把这些规则限定在「非 screen」媒体下;但绘制平装本 PDF 的引擎 WeasyPrint 以 print 媒体类型渲染,而 print 正是「非 screen」——完全符合规范。于是两条电子阅读器规则渗进了印刷版:提示框段落更紧的行高,以及一处 box-decoration-break 的改动,导致跨页的提示框丢掉了重复的内边距。两处都不可见,什么都没坏,但每个提示框都被削掉了一丝垂直空间,整本书从 576 印刷页慢慢瘪到了 567 页。他之所以发现,是因为印刷 PDF 的页数被钉死在 576 页、构建变红了——一个为保护封面书脊而写的页数闸门,抓到了一个它从未被设计去抓的 CSS bug。修复方式是在仅限印刷的区块里用 !important 大声地重新钉住原值。
设计与排版也有测试套件,由 Playwright 针对构建好的 HTML 运行,检查真正渲染出来的东西而不是他以为 CSS 做到的事。两类:可访问性(axe-core 在明暗两种模式下按 WCAG 2.1 AA 扫描整本书——无 critical 或 serious 违规、两种配色下都满足 AA 对比度、每张图都有 alt 文本、标题顺序合理、链接文字可辨识、声明 lang 属性以便屏幕阅读器选对发音);视觉与布局回归(断言实际计算样式符合设计意图——标题页居中且副标题字重更轻、目录是带 doc-toc role 的真实导航且链接可用、每种提示框有独一无二的左边框颜色和自动生成的标签前缀、小型大写是真正的 font-variant 而不只是一个类名)。「这些都不光鲜。这恰恰是那种在周五重构 CSS 时无声无息坏掉、直到读者发邮件来才浮现的东西。」
构建层。 核心是 Pandoc。但 Pandoc 只是引擎,完整构建还需要 WeasyPrint 画 PDF、Ghostscript 做转换,以及一堆 Noto 字体来覆盖完整 Unicode——一整套难搞的原生依赖,所以整个环境装在一个 Docker 镜像和 dev container 里,并钉死渲染器的精确版本(这个细节比想象中更重要,见上文页数闸门)。这个容器也是他能在 iPad 上写书的原因:重型工具链跑在 Codespaces 里,而不是他膝上。预处理阶段在源码树的一次性副本上进行(所以这些变换从不触碰真实章节):给每次构建打上 commit SHA、把行内链接转成印刷版的编号脚注(纸上超链接没用,但带完整 URL 的脚注有用)、按格式重排章节(EPUB 得到可分享的引文链接,印刷版和 Kindle 换成一个二维码分享页)。CSS 流水线是一份 Tailwind 样式表经 PostCSS 编译,屏幕、印刷、EPUB 共用同一份真相来源,再各取其一片:一个 strip-page-rules.js 为 EPUB 导出精简版,剥掉 Kindle 和 EPUBCheck 会呛住的 CSS 分页媒体特性。
他说自己没想到会爱上的部分是 Pandoc 的 Lua 过滤器:Pandoc 把一切解析成抽象语法树,允许你在渲染前用小 Lua 脚本重写这棵树,于是格式相关的调整活在代码里而不是抹在文稿里——删掉一个节点只需返回一个空表。他举的例子有:strip-comments.lua(丢掉编辑用的 HTML 注释,让笔记永不出现在已发布的 xhtml 里)、add-div-titles.lua(注入提示框标签和 DPUB-ARIA 可访问性 role,标签不必硬编码在每一章里,还能按格式不同)、三种 emoji 处理(彩色 emoji 在屏幕上没问题,strip-emoji-kindle.lua 为 Kindle 换成安全字形,因为电子墨水没有 emoji 字体;body-emoji-images.lua 为印刷 PDF 把它们变成行内 Twemoji 图片)、tagline-share-links.lua(在 EPUB 里每条「保险杠贴纸式」金句后追加一个「分享这个想法」永久链接)。
由此产生了「黑盒子」这个更微妙的 bug:Twemoji 的 PNG 是透明的,但透明像素底下的 RGB 是深板岩色——渲染器尊重 alpha 通道时不可见,不尊重时就是一个丑陋的炭灰方块,而是否尊重 alpha 完全取决于书在哪里被打开。他在 Kindle 上的第一版修法是把提示框背景色烤进每个 PNG 并干脆丢掉 alpha:在浅色模式的 Paperwhite 上完美,在确实尊重透明度、又不在图片后面绘制提示框底色的 Kindle for iOS 上糟糕——每个 emoji 变成了彩色矩形,「我把一个盒子换成了另一个盒子」。最终发布的版本保留 alpha,但把每个完全透明像素的 RGB 改写成白色:尊重 alpha 的渲染器得到干净透明,把它压平的渲染器得到白色,在白页上消失。印刷 PDF 需要的却是相反的修法:Ghostscript 用 -dNOTRANSPARENCY 把平装本转成 PDF/X-1a(为了让文字保持可选中的矢量而不是 300 DPI 位图,这个标志是必需的),而它总是丢掉 alpha;所以在那里透明才是敌人,每个 emoji 都被压平到它所处的确切颜色上(提示框的灰,或正文的白),完全没有 alpha,也就没有东西可丢、没有盒子会露出来。「同一个 bug 需要两种相反的修法——一个运行时可能丢掉 alpha,另一个保证会丢。」一个 Python 脚本在构建时把两套都烤好。「这是世上最普通的 bug,只不过我的这个恰好在一本书里。」
渲染与后处理按格式各走一条路,平装本走得最远:HTML 和 PDF 都经 Pandoc 渲染,但 PDF 由 WeasyPrint 绘制,「我用和做网页一样的盒模型排完了整本书」;EPUB 走另一条路,Pandoc 嵌入 WOFF2 字体子集,然后 postprocess-epub.js 重新打包以保持有效且小巧;平装本在别人都停下后继续走到 Ghostscript,变成 PDF/X-1a:2001——CMYK 色彩、嵌入 USWebCoatedSWOP ICC 配置文件、压平透明度——按需印刷发行商 IngramSpark 离不开的那个古老印刷标准。check-pdf-page-count.js 钉住页数,一旦平装本偏离 576 页构建就失败,「便宜的保险,防止过时的页数变成尺寸错误的封面书脊」。
构建的测试。 文风 linter 让文字诚实,Vitest 套件中更大的一片让构建诚实。一次 Pandoc 升级或一行乱入的 CSS 都可能静默地弄坏某个格式,而他要到读者的 Kindle 渲染出错误字体时才会知道。测试检查机械部分——每个变换是否做了它声称的事,以及(数量最多的一簇)排版是否在小型大写、表格边框、印刷对比度、深色模式和电子墨水 emoji 回退上悄悄漂移。「每一项都是一个测试,因为每一项都是我曾经见过构建看起来没问题却渲染错误的一种方式。」但单元测试只证明他的代码对,不证明输出有效,所以每个产物还要过一遍商店自己用的那些校验器,「拒绝发生在我的笔记本上,而不是上传日」:每个 EPUB 都过 EPUBCheck(每个商店在上传时都会跑的校验器)和 Ace by DAISY(专为电子书打造的可访问性审计);构建出的 HTML 由 lychee 爬取检查链接腐烂,并在畸形标记变成畸形电子书之前通过 HTML 校验器。这一切都不需要他记得去跑:每次 push 到 main 都会启动 14 个并行 CI 作业——先构建 CSS,然后并排构建 EPUB、Kindle EPUB、印刷 PDF、PDF/X-1a、HTML 和 DOCX,并在每个产物就绪时校验它。文中截图显示的作业清单是 lint-and-test、detect-duplicates、build-css、build-epub、build-kindle、build-pdf、build-html、build-docx、Build PDF/X-1a,然后是 validate-epubcheck、validate-ace、validate-links、validate-html、validate-playwright,全绿,总耗时 5 分 8 秒。因为每次构建都把 commit 戳在标题页上,这本书像软件一样有版本:他当时在 1.0.1 版,「有 tag 和 changelog,而不是一片 final_v3_revised_ACTUALLY_FINAL.docx 的坟场。一处错字修订就是一个补丁版本」。
出版层却几乎完全是手工点击。没有 git push 到生产。每个商店——亚马逊 KDP、面向书店和图书馆的 IngramSpark、面向 Apple Books 和 Kobo 的 Draft2Digital——都要你登录网页后台、手工上传 EPUB 和印刷 PDF,并把同样的元数据(标题、描述、BISAC 分类、关键词、价格)重新填进一个每次都略有不同的表单。「我的流水线构建出一个完美无缺的产物,然后我像 2009 年那样把它上传上去。」不过有三件事让这部分也保持诚实:他成立了 LLC 并自己买了 ISBN(每种格式一个),这样目录里列的出版者是他自己,而不是带零售商名字的免费平台 ISBN,也避免了免费 ISBN 只能通过发放它的平台出版所带来的锁定;选择「广泛发行」而非独占,跳过了给钱更多但把书锁死在亚马逊的 KDP Select;元数据也进了版本控制,描述、分类和关键词都在仓库里作为单一真相来源,可以 diff,并从中生成 ONIX feed 供接受这种格式的渠道使用,「我仍然手工把它粘进网页表单,但至少我粘的是一份受版本控制的文件」。
展望。因为内容是结构化的 Markdown,翻译更接近本地化一个应用而不是重新打一遍手稿:他已经有一条流水线(巴西葡萄牙语、拉美西班牙语、德语),会起草译稿、钉住每个标题 ID 以免交叉引用断掉、强制执行术语表以保持关键术语一致,并把结果回译以对照原文。有声书方面,如果他去录,把十万字变成朗读会带来一整类新 bug——发音错误——所以有声书有自己的 QA 套件:把生成的音频转写回文本并与脚本 diff、检查棘手词的音素、审计发音,全部接进各自的 CI 工作流,「一个瞄准我自己声音的快照测试」。他还想把这些工具(校验器、Lua 过滤器、整条流水线)清理后开源,「这样下一个人不必从零造」,并说这一切之所以存在,全靠它已经站在的那些开源项目——「整本书就是别人的慷慨编译而成」。
结论与数字表。词数约十万、约 70 章与小节;印刷长度 576 页(6 英寸 × 9 英寸);已发布格式 5 种(另加一个不发行的 DOCX);测试约 70 个文件、2204 个用例、约 5500 项自动检查;每次 push 14 个并行 CI 作业;文风规则约 300 条、覆盖 500 多个禁用词,之上还有一个 5000 条规则的语法引擎;提交 5000 多次;书 1 本(「坦白说过度工程化了,但很好玩」)。面对「这不就是把拖延症打扮成严谨吗」的自问,他的回答是:过度工程化,绝对是;但拖延不是——这五千次提交里只有大约五分之一碰到了构建工具,另外四分之四是书本身,「每一项检查都抓到了我否则会发出去的东西。会再来一次吗?毫不犹豫」。他还把整套装置指向自己这个博客十五年的存档,抓到了一篇 2011 年的文章一直把「it’s clear」写成「its clear」——正是他的 linter 现在每次敲键都会标出的那个缺失的撇号。「成千上万人读过它,没人提过。你不会知道。你必须去找。」他强调习惯也一起迁移了:把抓到过一次的错误当成永久抓它的规则;对任何机器起草的东西保持人在环中;让构建成为真相来源。最后的立场很克制:「我不会叫你为你的小说写 5500 个测试。对大多数书来说,这里的大部分都极不相称——而这正是重点。」他做是因为多一项检查、多一种格式、多一个校验器的边际成本只是几分钟和一点好奇心,也因为用开发者的方式做,让他能在离开办公桌的 iPad 上写作并信任 main 永远可发布。「出版业从文字处理器开始。我从一个 Git 仓库开始——我还会再从那里开始。」
HN 评论精华
这条帖子只拿到 53 分、47 条评论,是这一组里分数最低的一篇,而且讨论几乎完全跑偏到了一个作者本人没预料到的方向:评论者们认定这篇文章(以及这本书和它的营销落地页)读起来像 AI 生成的。围绕这一点爆发了本帖最长、最激烈的争论,作者 benbalter 全程在场并做了相当坦诚的回应。另一条支线是善意的「这是结构化拖延症的教科书案例」的调侃,以及几个人分享自己类似的工具链。
- 帖子的第一条评论就设定了敌意基调。hyperhello 说:「行吧,但我很确信你的书会无聊得惊人。」benbalter 自嘲回应:「你以为我为什么要靠搭这一堆不必要的工具来拖延写作呢?/s」NAR8789 注意到作者自己那条描述性的评论被降票、而唯一另一条评论是在贬他,「看来你让人不舒服,但我搞不懂你为什么招来这种程度的恨。这是你的常态吗?有人跟你有仇?」hyperhello 澄清:「你完全想歪了。恨很荒谬,我根本不认识他。他被自动降票是因为在 HN 上推自己的产品,虽然他确实没推得太狠。」
- stephantul 第一个点出核心质疑:「有点讽刺的是,我发现这篇文章非常难读。我对内容超级感兴趣,但它读起来像是生成的。」benbalter 的第一反应是打趣(「以前也有人这么说。当过律师,永远是律师」),然后做了完整披露:几个月前他有些 token 要过期,让 Copilot 起草了一版看看有没有值得写的东西;「如果你查修订历史(文末有链接),我三天前做了一次完整的(人工)白纸重写,之后只用 Claude 做加链接、修 lint 失败这类逐行编辑」。japhyr 立刻抓住了措辞漏洞反问:「你三天前对一本 576 页的书做了『白纸重写』?」
- sinab 的批评最具体,也最有分析性。他先感谢了这个演示,然后说他个人觉得作者设计出的这种写作风格读起来强烈地像 AI 生成,举例是像「Here’s the irony: after automating everything up to this point」和「Turning emoji into images solved the missing-font problem and created a subtler one」这样的段落开头。「这两个都是 AI 散文的强烈信号,尽管我发现很难说清究竟为什么。这让我怀疑到底是你的风格演化得更像 AI 散文,还是 AI 散文演化得更像你。」被指责为在打压人类写作后,他做了更细的拆解:他从未声称能判定是不是 AI 写的,也不是想劝人别写(恰恰相反);他观察到的是在 LLM 散文中变得相当常见的、可识别的过度使用的文体模式——仅这一篇文章里就有三处「here’s the X」式修辞路标、十几处工整构造的反转/对偶(「不是 X 而是 Y」「解决了 X 又制造了 Y」)、反复出现的「铺垫→复杂化→抖包袱」段落结构、用修辞性问句做过渡、以及高密度的拟人化技术隐喻。他强调这些单独都不能证明是 AI 写的,而它们的存在(以及作者自己的话!)恰恰就是他要说的:作者的 validateHypotheticalHooks linter 就是在标「公式化的 AI 味开场」,而他描述自己被抓到时说的是「我从读太多生成文本中吸收了那种韵律」。
- KPGv2 的反驳最激烈:「『嗯实际上你的写作看起来像 AI 而这好有问题』摇手指——你们这些人听起来就是这样。你识别不出 AI,没有人能,除非专门受训去识别,而即便如此,也只在网上语料远小于英语的语言中被证明有效。LLM 在这世上最擅长的事就是模仿人类写作。所以你真正在做的只是打消一个人写作的念头。」他自称在艺术领域最恨 AI,「没有人比我更恨」,但「我已经看了一年多合法作者被指成 AI,糟到我知道有人因此放弃写作(『有什么意义呢,大家以为我是 AI 就不读了』),最终结果是写作中 AI 的比例上升,而人类享受最人类的这门学科的比例下降」。
- soulofmischief 是本帖批评最系统、也最尖锐的一位,他对 KPGv2 的回应是:「抱歉,但如果你在写作和使用现代 LLM 两方面都还算在行,这份材料里每一个特征都在。」他随后写了一整套指控:看看这本书的落地页(open-and-async.com)——文章和书都是 vibe-written(用 AI 随手写出来的)。数数它吹嘴具体数字的次数。数数虚假洞见的数量、那些把显而易见的东西抬升为深刻的句子。数数破折号的数量。数数无生命物体或动词被当作名词用的次数。看看那教科书般的 Claude 设计,满是发光效果,体现了 web 2.0 落地页文化最糟糕的一切。他引用了落地页上的句子作为例证。他说最令人警觉的部分是「Ben 要么没有费心审阅和编辑内容,要么不够在行到能认出它有缺陷。这没阻止他用自己的账号直接发到 Hacker News,甚至没标 Show HN。他认为这份材料已经可以面向受众,这让我担忧」。在另一条更长的评论里他把质疑推到根本:「所以这本书到底是谁的?本来可以是一堂很有用的课,现在却像是一匹为推销一本书而做的自动病毒营销垃圾的特洛伊木马——而我此刻只能假设那本书也是 vibe-written 的。」他区分了 AI 辅助工作与 vibe-coded 工作,问「你对这个项目施加了多少编辑控制?想法从哪来?我怎么知道某个片段是经过充分研究的事实,而不是未经证实的潜在幻觉?」他说自己也做网站几十年了,「而我认识的这一类人里,大多数更有兴趣让自己辛苦挣来的经验把作品顶到日益激烈的竞争中的最上层」。
- benbalter 的回应是本帖最有价值的部分。他先承认这是这里最有意思的反馈,「尤其因为我在博客文章里做了同样的观察」,并重贴了那段关于 validateHypotheticalHooks 抓到他自己的话。对于「你是人类」的举证责任,他说「『相信我,我是人类』在这里行不通(我们在互联网上证明人性的方式,字面上就是训练机器人更像人)」,然后给出了他的实证路径:这本书的大部分章节起源于他发表了十多年的博客文章,远在 AI slop 出现之前;「2026 年的我没法用终结者手段给自己造一条追溯的纸质轨迹」。在另一条回复里他把对应关系逐条列了出来,包括基于早期同论点文章的章节(「Why URLs?」2015 + 「Expose process through URLs」2014 →「为什么一切都该有 URL」;「Write corporate blog posts as a human」2015 →「像人一样说话」;「On stickers and optimizing for happiness」2015 →「为开发者幸福感优化」;「The zen of GitHub」2015 →「开放与异步工作之禅」;「Manage like an engineer」2023 →「像工程师一样领导」;「Deprecate management」2012 + 「Cathedral–bazaar management」2023 →「为远程团队重新思考管理」;「Word vs. Markdown」2014 + 「We’ve been trained to make paper」2012 →「重塑你的文档」;「Eight things I wish I knew my first week at GitHub」2016 →「转远程前我希望知道的事」等等),以及近乎逐字承袭同名文章的章节(「Leaders show their work」2022、「Work loudly」2026、「Meetings are a point of escalation」2023、「Transparency & collaboration is the Andon of knowledge production」2023、「Dissenting voices」2024、「One-on-one playbook」2026、「Agentic workflows」2026、「Why async」2022)。他还说会「今晚再过一遍这篇文章和网站」,并把被标出的那些句子改掉,「这样它们不会削弱信息本身」。他也澄清:多渠道上架完全是手工的,他从没出版过书,所以只是(非 agentic 地)问过 AI 关于流程的问题(该用服务 X 还是 Y、要不要做预售);公开面向读者的内容「100% 人在环中」,他想让它以读者为中心而不是以营销/作者为中心,「我觉得后者才是 LLM 被训练出来的常态」。Ar-Curunir 补了一刀:「老兄,连你的评论都听起来像 LLM 写的。」
- soulofmischief 追问了一个买家视角的好问题:「我好奇你觉得 2012 或 2014 年的经验和实践,在一个快速变化的后 AI 环境里还有多少成立?」benbalter 回答说书里没太多谈 AI(因为它变得太快),但反思起来很有意思的一点是2012/2014 年的那些基本功(Markdown、URL、API 等)在我们开始考虑机器可读性时,权重加倍了。soulofmischief 还说:「作为一个『一本书及其营销活动可以用程序化、自动化的方式从头到尾执行完』的概念验证,这是个有意思的作品……我几乎更想读一本关于这个过程的书,而不是这本实际的书。」
- thallada 提出了本帖最有洞察力的一个假设:「我确实怀疑,所有这些自动化 linting 和 LLM 驱动的编辑辅助,是不是正在促成你那非常像 LLM 的写作声音,触发了这里所有人内置的 AI 写作检测警报(另外,Pangram 检测这篇文章约 20% 是 AI 写的,仅供参考)。这说得通:linter 会打红波浪线的那些东西(比如弱的被动语态),也正是被从 LLM 里训练掉的、不够『有力』的那类散文。所以,基本上,你是不是不小心把自己 RLHF 成了一个 LLM?」pushcx 也在同一方向补了一句妙语:有一个专门抓「读起来像机器生成而非我的声音」的 AI-tells linter,「真有一种『上帝要飞船干什么?』的回响」。everybodyknows 指出另一个信号是过度戏剧化,举的例子是「我不会叫你为你的小说写 5500 个测试。对大多数书来说,这里的大部分都极不相称——而这正是重点。」turtlebits 说:「我平时不评论 AI 文章,但那个 TLDR 太明显是 AI 了,太让人反胃,我直接关了标签页。」Paracompact 写了一段模仿 AI 助手腔调的讽刺短剧,取笑那个「我不在 GitHub 工作了」的正则校验器的来历。
- 除了 AI 争议,还有几条正面和实用的讨论。hinkley 分享了一段很有分量的往事:他曾看过 Bruce Eckel 在《Thinking in Java》成为畅销书后的一次演讲,本以为会讲 Java,结果 Eckel 讲的是他如何「过度工程化」这本书——他让实习生解决一个问题:如何确保书里发表的代码示例被抄进编辑器后真的能跑;他们写了一个工具,给可运行代码标记抽取点,自动摘录进手稿。大约五年后,hinkley 在一家《财富》50 强公司和一群「把事做成」的承包商共事,让周围一些官僚化的组织单元感到不安;有人以「你们的开发者文档不符合公司定义的文档标准」为把柄发难。他们没错,但熟悉那个平台的人根本不会被现有文档难住;如果按对方建议的方式做,每次发布都要多花他将近一周的时间,而他当时已经在为「如何把工作分派出去以免自己成为瓶颈」而挣扎。「但 Bruce 救了我。」他发现 Eckel 的策略已经被做成了一个 FOSS 工具 jcite,于是第一季度花了大约 50 小时学会用它并改造所有集成测试;此后每次发布修文档只需不到一小时,其中一半还被一位技术写作者接手了(他没能让文档模板完全匹配公司模板,她手工修掉那几个错误)。「所以再过一个季度我们就回到了全速。我可能是想象出了我们解释我们做了什么时对方的失望,但如果是想象,那也是一次美味的幻觉。想得美。也许你可以向我们学习,而不是想把我们拉下去,嗯?」benbalter 回应说他(AI 出现前)的规则一直是「绝不让人做机器人能做的事」,「如果你能把工作中最讨厌的部分自动化掉,那对你和雇主都是赢」。hinkley 补充:「如果你把它开源,回报倍数还能再高一两个数量级,至少能在下一份工作里用同一个方案。」
- khaki54 给出了本帖最精炼的调侃:「结构化拖延症的教科书案例。我打赌你把家里所有项目和明年的税都做完了。」sixtyj 认领道:「贼认贼……;) 我在 OP 的记述里看到了我自己,过程写得很好。」_def 提出一个语义质疑:「人们是怎么得出『过度工程化』是正面词的结论的?」epgui 反问是什么让他觉得「人们」有这种结论,并在被引用原文后说:「在我听来更像是承认而不是自夸。」thereisnospork 则给出一个工程学辩护:从消费者视角看它是正面的(哪怕只被理解为「不是欠工程化」)——过度工程化的东西留下了余量给消费者利用,而现实世界中的物件常常承受超出理想设计约束的载荷和需求。
- 几位分享了自己的做法。t-kalinowski 推荐了自己写的 yamark,benbalter 回复「我的周末就这么没了,谢谢分享」。song_synth 说自己从文章里学到「能做网站就能做电子书」,「这很鼓舞人,因为我确实能做网站,而且有一堆想变成书的草稿」;benbalter 回应「很高兴能激励到你,这就是这篇文章的目的。声明一下,我从没保证过这本书(或那个网站)好。软件也好文稿也好,你可以有世上最完美的构建流水线,但正如人们说的,垃圾进垃圾出」。everybodyknows 讲了自己相反的路径:他也想用 FOSS 写一本高度结构化的书,需要 Git、某种层级化纯文本格式和一个视觉上丰富的编辑器;Markdown 满足不了第三点——进度相关的、最终会被剥离的注释是必需的,而 Markdown 的 HTML 式注释「难用到无法忍受」,编辑器也不知道该怎么弱化显示别的语法。他最终用了 Org-mode,Emacs 稍加配置就能丰富地上色和分字体,Org 文本进入他用 Go 攒的一个 Org-to-HTML 转换器;最近完成的作品是给亲戚做的相册,HTML 形式可以放上网,但他用 WeasyPrint 生成了自包含的 PDF 作为纪念品。whiterook6 说自己写短篇小说,用 Astro 构建站点,为每篇和整本都生成可分享的二维码和 epub,用 gh-pages 推到 GitHub Pages;「我懂你说的过度工程化。我感觉自己像个 Linux 用户——花更多时间把笔记本调到刚好,而不是用它。我得提醒自己重点是结果,不是过程」。goostavos 贴出了对照组:「相反,这是我如何欠工程化我的书 lol」(HN 49336392),benbalter 回应「哈!确实。喜欢那个延时视频」,并补了一句「得把『Hacker News 头版上的疯子』加进我的简历」。cadamsdotcom 指出自定义 lint 现在便宜到没理由不做,附上一段可以直接丢给 agent 的 prompt:让它在仓库里加一个 pre-commit hook,打印所有出现带连字符 open-source 的文件名和行号并非零退出,装进 .git/hooks,「敲下回车一分钟后,你就给自己造了一个保证:这个仓库再也不会出现 open-source 这个字符串」。nonethewiser 一句话总结:「软件工程的工作流对基于文本的工作来说就是史上最强。」frizlab 引用那句「AI 审阅了每一页;每一个字都是我写的」说:「在我看来这是 AI 唯一正确的用法。包括在开发中。」basedrum 在帖末问了个实用问题:「这些的源码在哪能找到?我正在过度工程化我自己的书,你有些有意思的想法我很想复用,不想重新实现一遍。」