Refactoring English: Month 9

Michael Lynch

Refactoring English:第9个月

一句话总结

剪辑视频访谈的快乐与痛苦

亮点

  • 我从早期读者那里收到了关于章节列表的有用反馈。
  • 剪辑访谈视频让我倍感沮丧,但制作文字稿却很有趣。
  • 推广我的自由职业博客编辑服务的计划比预期的要顺利。

目标评分

每个月初,我都会公布本月想要完成的目标。以下是完成情况:

给20位从未交流过的读者发送个性化邮件

  • 结果:决定改为做一份章节调查
  • 评分:不适用

通过与几位读者的交流,我意识到现阶段更好的策略是对所有读者进行一次广泛的调查

发布 Refactoring English(《重构英语》)的新章节

  • 结果:发布了“Get to the Point”
  • 评分:A

我终于完成了关于引言的章节。这是最难写的一章,因为引言是我写作中最具挑战性的部分。因此,这既是一篇引言,也是我尝试逆向拆解自己如何写引言的过程。

这一章也远远超出了预算。我最初只预算了六小时来完成它,但最终花了19个小时。

完成我剩余的营销任务

  • 结果:完成了访谈剪辑,但未完成行动号召
  • 评分:B+

我完成了访谈的剪辑,这是最大的未完成任务。我还没有抽空调整书的网站设计,将重点从订阅免费邮件列表改为购买抢先体验版。

《重构英语》指标

指标2025年7月2025年8月变化
独立访客8,0612,863-5,198 (-64%)
预售收入$800.04$312.63-$487.41 (-61%)
赞助收入$48.25$48.25$0.00 (0%)
总收入$848.29$360.88-$487.41 (-57%)

上个月,我觉得这本书的各项指标还算健康。尽管没有发布热门新文章,网站访问量和收入都在上涨。

此后,我为指标添加了图表,现在看到了不同的模式。这本书的收入似乎与书的网站访客数密切相关。虽然我以为7月并没有热门文章,但8月当我没有在书的网站上发布任何内容时,各项指标便急剧下滑。

我的收获是,我确实需要持续在网站上发布新内容,才能不断找到新读者,尤其是愿意为这本书付费的读者。

解读读者对章节列表的反馈

在与读者的个别交流中,大家认为相关的章节差异很大。我觉得,要了解读者最关心哪些章节,更好的办法是发起一次调查。

我原本预期回复率会很低,因为我以前在邮件列表中征求过反馈,只收到寥寥几条回复。让我意外的是,读者对这次调查要热情得多,两周内收到了133份回复。

我在书的网站上对这些回复做了详细分析:

简而言之,我获得了有用的反馈,它促使我重新排序了章节,并重构了读者不喜欢的一章。

这次的回复率与以往征求反馈时的差异也很有意思。以前我在发送样章后征求反馈,我认为差别在于我要求读者投入的工作量。这次调查几分钟就能完成,而要对一章给出反馈,则需要花30分钟阅读章节并思考,也许你在收到调查时还没准备好做这件事。

剪辑30分钟视频访谈的意外困难

早在2024年7月,作为重启我的博客课程Hit the Front Page of Hacker News的一部分,我录制了与Adam Gordon Bell(亚当·戈登·贝尔)的一次访谈。结果在休陪产假前我没能完成课程,于是便无限期搁置了重启计划。

这让这次访谈陷入了尴尬的境地。亚当·戈登·贝尔慷慨地抽出时间接受采访,我若完全不发布访谈会感到愧疚。

当我开始为《重构英语》提供抢先体验时,我觉得是发布这段访谈的好时机。如果人们喜欢这段访谈,也许会去看看这本书。

你知道那种被你无限期推迟的任务,你会想:“我已经拖了这么久,真是太傻了,只要坐下来做,一个小时就能完成,就不用一直惦记着了。”我当时笃定这次访谈就会是这样。

结果并非如此。

音频偏移顽疾的回归

我使用名为 Riverside 的服务录制了访谈。通话结束后,Riverside 为通话双方分别生成了视频文件,以及一个合并、同步后的会话版本。我当时只是抽查了视频以确认可用,但从未仔细观看。

我以为工作就是拿着合并好的版本直接上传到 YouTube。也许如果有中断或过长的离题,我会剪掉,但我觉得视频已经基本完成了。

当一年后我终于坐下来仔细观看视频时,才发现音频和视频不同步。在视频中,你能在嘴唇动作之前就听到我们的声音。

在 Riverside 生成的视频中,音频和视频略有不同步。

好吧,没问题。我可以用FFmpeg重新处理视频,稍微平移一下音频。

不行,事实证明对话两端的音频偏移并不相同。亚当那一端偏移了约425毫秒,而我这一端约150毫秒。这意味着我必须回到对话双方各自原始的、未合并的视频,分别修正偏移,然后再自行重新合并。

寻找可用的开源视频编辑器

我以前编辑视频的标准工具是 Adobe Premiere,但我去年换到了 Linux,而 Premiere 不支持 Linux。而且,我现在已经对 Adobe 这家公司感到厌烦

我开始在 Shotcut 中剪辑视频,这是我去年夏天一直在学习的一款视频编辑器。花了好一会儿才弄明白如何在 Shotcut 中把视频并排放置,但最终我用缩放和裁剪滤镜拼凑出了效果。

在 Shotcut 中编辑时,播放极其卡顿,因为即便是在我那台相当新、配置很高的台式机上,合并两个1080p视频时也会卡死。所以,为了听清视频实际的声音,我不得不导出视频。而 Shotcut 不支持只导出视频的一部分,所以每次我都要导出完整的30分钟,每次都要花上好几个小时。题外话:我后来才发现可以在 Shotcut 中降低播放分辨率以在编辑时获得更流畅的性能。

导出后我注意到,每次我分割一个片段,Shotcut 都会插入一声响亮的爆音。即使我在分割点实际上没有剪掉任何内容,也会发生。仅仅是将一个连续片段分割成两个相邻片段的动作,就会产生这种爆音伪影。

仅仅分割片段而不做任何修改,就会在音频中产生“爆音”。

我发现这些爆音是 Shotcut 中已知的问题,这让我难以置信。每次分割都会加入令人分心的音频伪影,还怎么剪视频?但许多评论者说每个视频或音频编辑工具都有同样的问题。

什么?!?

我用其他工具编辑过数百个媒体文件,从未见过有工具在分割片段时会插入爆音。

我尝试了其他适用于 Linux 的开源视频编辑工具。Kdenlive在我尝试编辑几分钟后就崩溃了。Flowblade根本无法加载,但我最终找到了一个变通方法。Flowblade 看起来像是 Shotcut 的简化版,所以我又在 Flowblade 中重新开始了剪辑过程,还得弄明白如何创建并排视频。

在 Flowblade 中编辑了约一小时后,我尝试导出视频,爆音又出现了。他们也有关于这个问题的缺陷报告,而他们对此的理解基于 Dan Dennedy(丹·丹内迪)的解释,而他……正是 Shotcut 的作者。而且 Flowblade 似乎是构建在 MLT 之上的,正是驱动 Shotcut 的同一个媒体框架。所以,我又回到了完全相同的缺陷上。

总之,这段剪辑历险的叙述已经又长又无聊了,所以直接跳到结尾:我最终通过将视频的音频采样率从44.1 kHz转换为48 kHz绕过了爆音问题。这消除了爆音伪影,但我不知道为什么会这样。

在剪辑视频时我还遇到了许多其他缺陷,但在这里复述就太过繁琐了。

视频剪辑心得

  • 尽可能使用 FFmpeg 脚本进行预处理。
  • 保存 FFmpeg 脚本,以备日后需要调整预处理时使用。
    • 即使你非常确信不会再需要任何预处理,也要保存脚本,最好纳入版本控制。
  • 用极端值测试 FFmpeg 脚本,以确认它们在按你预期工作。
    • 我尝试通过平移200毫秒来校正音频偏移,但仍然不同步。然后我试了300毫秒,依然不同步。接着是400毫秒。我最终直接跳到2000毫秒,才意识到脚本中肯定有缺陷,因为200毫秒和2000毫秒的校正听起来完全一样。
  • 当不确定正确设置时,用脚本让 FFmpeg 生成多个不同版本,以便比较各种选项。
    • 我就是这样测试了多种消除我这端背景噪音的策略。
  • 44.1 kHz 的音频采样率显然会在剪辑时引发问题。
    • 在预处理期间将采样率转换为48 kHz修复了爆音伪影。
    • 我完全不知道为什么。
  • 在剪辑初期就尝试导出视频,以检查最终输出效果。
  • 将视频/音频编辑滤镜应用在轨道层面,而非片段层面。
    • 即使你一开始只有一个巨大的片段,一旦开始剪辑,就会有数十个具有独立滤镜设置的片段。
    • 如果你发现滤镜设置错了,就不得不在每个片段中重新设置。

沉浸在访谈文字稿的趣味中

当我完成与亚当·戈登·贝尔的视频剪辑后,本该就此结束,对吧?我花了那么多时间剪辑视频,肯定迫不及待想发布并收工了。

错了!

视频剪辑完成后,就该纠结文字稿了。不过,这部分我倒是乐在其中。

我感觉网上看到的每一份访谈文字稿,设计师的想法都是:“让我们把60年前打字机打出的法庭笔录原封不动地搬到网上,保留同样程度的趣味性和交互性。”

如果我们能用浏览器让对话文字稿变得更有趣,会怎样?

拜托!让我们用网络去做打字机做不到的事情。

于是,我用whisper-ctranslate2生成了初始文字稿,并花了大量时间让它变得准确、具交互性且阅读有趣:

我为访谈文字稿添加了小功能,让阅读变得有趣。

  • 对话的每一方都以不同颜色的气泡显示,一眼就能看出是谁在说话。
  • 每个气泡中都有小的播放按钮图标。点击图标时,页面会滚动到视频并从文字稿对应的时刻开始播放。
  • 我把最喜欢的引言提取出来做成了醒目的引用块。
  • 我添加了标题来帮助梳理讨论的结构。
  • 我校对了文本中的转录错误。

我还没有发布这个视频,因为我刚在周五向邮件列表订阅者发送了新章节。它将在本周末前(2025-09-12前)发布在书的博客上。

帮助 Tyler Cipriani(泰勒·奇普里亚尼)登上 Hacker News 榜首

有时候,计划的进展甚至比我所希望的还要顺利。

为真实的作者提供反馈有助于我写书,所以我一直在为其他独立开发者博主提供自由职业编辑服务。在介绍我编辑服务的页面上,我想放一个编辑工作样例,但我不想要求付费客户把他们已经付费的作品拿来为我做宣传。

所以,我的计划是找一个人,让我免费编辑他的文章,作为交换,我可以发布编辑笔记,而他则署名我是编辑。

我的延伸目标是这篇文章能在我书的潜在读者聚集的地方获得关注,比如 Hacker News、Lobsters 和 reddit。如果读者读到文章末尾看到“Edited by 《重构英语》”,他们会想:“嘿,那是什么?”

如果我能指着一位过去的客户对潜在客户说:“看,这个人雇了我,而他的文章在你想取得成功的地方获得了成功。”这也会很好看。

几个月前,泰勒·奇普里亚尼聘请我对他的博客进行一次高层次的审阅。他似乎对结果很满意,于是我向他提出了免费编辑的想法,他同意了。

我与泰勒·奇普里亚尼就他的文章“The future of large files in Git is Git”进行了几轮反馈。我们合作得很愉快,这也为我的书提供了很好的思路。

我的额外目标只是让文章登上 Hacker News 首页,但它的表现远超于此,一路冲到了Hacker News 第1名Lobstersreddit

我们两人最大的收获之一是针对目标受众调整写作的重要性。泰勒·奇普里亚尼文章的早期草稿假定读者熟悉Git LFS,这是一个用于管理大文件的 Git 扩展。

我提出,普通的 Git 用户不一定对 Git LFS 有足够的了解来理解文章中的所有内容。泰勒·奇普里亚尼对此有不同意见,因为他觉得处理过大文件的普通 Git 用户一定会了解 Git LFS。

为了让泰勒·奇普里亚尼相信读者对 Git LFS 的了解比他文章假设的要少,我列出了我对 Git LFS 的了解和经验:

  • 如果我的 Git 仓库中有大文件,或者我频繁更新仓库中的二进制数据,我就应该使用 Git LFS
  • 我从未使用过 Git LFS
    • 我也许参与过1到2个使用 Git LFS 的开源项目,但从未接触过任何 LFS 相关的部分。
  • 我不知道各个代码托管平台的大小限制是多少,但我以为如果触及限制,平台会给我提示,到时再处理
    • 如果我想在 Git 仓库中存储一个大于5 MB的文件,我就会开始寻找避免这样做的方法
  • 我以为 Git LFS 是一个与托管平台无关的功能,可以在任何地方使用。
  • 我以为 Git LFS 是一项已有10多年历史、成熟且稳定的技术
  • 我不知道一旦开始使用 Git LFS,你的仓库就会被它套牢
  • 我会对在 Git 中存储大文件的方法感兴趣,也会在 Hacker News/Lobsters 上点击相关文章,但除非我真的遇到需要在 Git 仓库中存储大文件的情况,否则我不会主动去思考并尝试解决这个问题。

泰勒·奇普里亚尼说,这份清单对他而言是“恍然大悟”的时刻。他之前抵制这个反馈,是因为他确信读者会了解 Git LFS。看到我的清单后,他意识到即使读者表面上知道 Git LFS 及其用途,他们也可能不知道它是如何工作的。

泰勒·奇普里亚尼这个领悟的巧妙之处在于,他自己本就可以写出我的这份清单。他对目标读者会知道什么有同样的预测;他只是需要再深入一层思考,读者“了解”一项技术到底意味着什么。

支线项目

Hacker News Observer 切换至 time-series database(时序数据库)以实现500倍提速

过去几年里,我听人们谈论“time-series database(时序数据库)”,但我从未理解它们是做什么的,也不知道它们与普通关系型数据库有何不同。去年我甚至在一个支线项目中使用了 InfluxDB,因为我需要一个与 Grafana 兼容的数据库。但我仍然不明白是什么让它成为“时序”数据库,或者为什么不能直接用 SQLite。

最近我和另一位开发者交谈,他提到使用 time-series database 来实现数据的不同视图,比如秒级粒度与天级粒度。他甚至没有再多解释,但我脑中灵光一现,心想:“哦!那一定就是时序数据库的用途!”

Hacker News Observer 是一个支线项目,它每分钟查询一次 Hacker News,并记录过去几周内每篇报道的点赞数、评论数和排名。我希望能深入挖掘并找到有趣的模式,但目前我只是在查看高层次的聚合数据,比如首页的总点赞数和评论数:

切换到 DuckDB 后,此页面速度提升了500倍

我最初使用 SQLite 作为数据库,上图需要两分钟才能渲染。这也说得通,因为每天有数千篇报道,每篇报道有数千个快照,然后我必须在每个快照中找到前30名(首页),再将它们放入按小时分桶的区间。SQLite 没有专门用于将每分钟数据聚合成小时级视图的函数,所以需要大量耗时的查询。

一旦理解了时序数据库的概念,我便向一个 LLM 询问了类似于 SQLite 的 time-series database 选项,它推荐了 DuckDB。然后我就让 LLM 将我的数据库从 SQLite 迁移到 DuckDB。仅这一次迁移,就将图表的加载时间从两分钟降至250毫秒,提速约500倍。

所以,我想这就是时序数据库的用途吧。

Gleam Chat Log Parser一点点处理旧日志

在我的聊天日志解析器项目上只取得了些许进展。我处理了包含离开消息的日志带有 Windows 风格行尾(\r\n)的日志。奇怪的是,在 Erlang(因此在 Gleam 中也是如此)中,\r\n算作单个字符,这让我困惑了一阵子。

收尾

完成了什么?

经验教训

  • 在剪辑视频时要更加自律
  • 剪辑访谈视频很乏味,但编辑和美化文字稿却很有趣。
  • 如果只需要几分钟而非30分钟的工作量,愿意提供反馈的客户数量会多一个数量级。

下月目标

  • 发布一些能为《重构英语》网站吸引新读者的内容。
  • 发布《重构英语》的新章节。
  • 给20位从未交流过的读者撰写个性化邮件。

原文由 Michael Lynch 发布

本文章由 muse-spark-1.2-contributor 进行翻译