Refactoring English: Month 9

Michael Lynch

重構英文:第 9 個月

原文由 Michael Lynch 發布,訂閱此部落格

一句話總結

剪輯影片訪談的喜與悲

重點回顧

  • 我從早期讀者那裡獲得了關於章節清單的實用回饋。
  • 剪輯訪談影片的過程令人沮喪,但製作文字逐字稿卻很有樂趣。
  • 我推廣自由接案部落格編輯服務的計畫比預期順利。

目標評分

每個月月初,我都會宣告當月想完成的事。以下是這次的達成狀況:

寫個人化信件給 20 位未曾交流過的讀者

和幾位讀者聊過後,我意識到現階段更好的策略是對所有讀者做一次廣泛的問卷調查

發布《Refactoring English》的新章節

  • 結果:發布了「Get to the Point」
  • 評分:A

我終於完成了關於開場的章節。這是我寫起來最困難的一章,因為開場正是我覺得寫作中最具挑戰性的部分。所以,這一章既是談開場,也是我試圖逆向拆解自己如何寫開場的過程。

這一章也遠遠超出預算。我原本只預估花六小時完成,結果實際花了 19 小時。

完成剩餘的行銷任務

  • 結果:完成了訪談剪輯,但還沒處理行動呼籲
  • 評分:B+

我完成了訪談的剪輯,這是當時最大的待辦事項。我還沒抽出時間調整書籍網站的設計,將重點從訂閱免費電子報轉向引導購買搶先體驗版。

《Refactoring English》數據指標

指標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%)

上個月,我覺得這本書的數據還算健康。儘管沒有熱門新文章,網站造訪量和營收都還在成長。

後來我為這些指標加上了圖表,現在看到了不同的趨勢。這本書的營收似乎與書籍網站的訪客數高度相關。雖然我原本以為七月並沒有什麼熱門文章,但當我在八月完全沒有在書籍網站上發布任何內容時,數據就直接崩跌了。

我的結論是,我確實需要持續在網站上發布新內容,才能不斷找到新讀者,尤其是願意付費買書的讀者。

解讀讀者對章節清單的回饋

在與讀者一對一的對談中,大家覺得相關的章節差異很大。我覺得要了解讀者最在乎哪些章節,更好的方法是發送問卷調查。

我原本預期回覆率會很低,因為之前在電子報上徵求回饋時,往往只收到零星幾個回應。讓我意外的是,讀者對這次問卷熱情許多,兩週內就收到了 133 份回覆。

我在書籍網站上對這些回覆做了詳細分析:

簡單來說,我獲得了很有用的回饋,並因此重新排序了章節,也重塑了讀者不喜歡的那一章。

這次回覆率與以往徵求回饋的差異也很有意思。過去我都是寄出試讀章節後才請大家給回饋,我想差別在於我要求讀者投入的心力不同。這份問卷幾分鐘就能完成,而要針對一個章節給回饋,則得花 30 分鐘閱讀、思考,或許收到問卷的當下,讀者根本還沒準備好投入那麼多時間。

剪輯 30 分鐘影片訪談的意外困難

早在 2024 年 7 月,我為了重啟我的部落格課程 Hit the Front Page of Hacker News 而錄製了與 Adam Gordon Bell 的訪談。結果在請育嬰假前沒能完成課程,所以重啟計畫就無限期擱置了。

這讓這段訪談處於一個有點尷尬的位置。Adam 好心撥出時間接受訪問,如果完全不發布,我會覺得很過意不去。

當我開始提供《Refactoring English》的搶先體驗版時,我覺得正是發布這段訪談的好時機。如果大家喜歡訪談,或許也會去看看這本書。

你知道那種一直拖著不做的事嗎?你心裡會想:「都拖這麼久了,真的很蠢,只要坐下來做,一小時就能完成,就不用一直掛在心上了。」我本來很確定這段訪談就是這種事。

結果完全不是那麼回事。

音訊偏移夢魘的回歸

我使用名為 Riverside 的服務錄製訪談。通話結束後,Riverside 會為通話雙方各自產生影片檔,以及一個已合併、同步的對話版本。當時我只有抽樣檢查影片是否能播放,但沒有仔細看過。

我以為只要拿著合併好的版本直接丟上 YouTube 就好。頂多如果有中斷或冗長的離題再剪掉,我以為影片已經近乎完成了。

直到錄完一年後,我終於坐下來仔細看影片時,才發現音訊和影像不同步。你會先聽到我們的聲音,然後才看到嘴型對上。

在 Riverside 產生的影片中,音訊與影像有些微不同步。

好吧,沒問題。我可以用 FFmpeg 重新處理影片,把音訊稍微位移一下。

結果並非如此。事實證明,對話兩端的音訊偏移量不同。Adam 那端偏移了約 425ms,而我這端則約 150ms。這表示我得回到雙方各自的原始、未合併影片,分別修正偏移後,再自行重新合併。

尋找可用的開源影片剪輯軟體

我原本剪影片的標準工具是 Adobe 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 腳本,確認它們真的如你所想在運作。
    • 我試著將音訊位移 200ms 來修正偏移,但還是不同步。接著試 300ms,還是不同步。然後試 400ms。最後我直接跳到 2000ms,才發現腳本一定有錯誤,因為 200ms 和 2000ms 的修正聽起來完全一樣。
  • 當你不確定哪個設定才正確時,就用腳本讓 FFmpeg 產生多個不同版本,以便比較選項。
    • 我就是這樣測試不同策略來消除我這端對話的背景噪音。
  • 音訊取樣率 44.1 kHz 在剪輯時似乎會造成問題。
    • 在前置處理時將取樣率轉換為 48 kHz 就能修掉爆音瑕疵。
    • 我完全不知道為什麼。
  • 盡早在剪輯過程中嘗試匯出影片,以檢查最終成果。
  • 將影音濾鏡套用在軌道層級,而非片段層級。
    • 即使一開始只有一個大片段,一旦開始剪輯,就會變成數十個各自有獨立濾鏡設定的片段。
    • 如果你發現某個濾鏡設錯了,就得在每個片段上重新調整。

玩訪談逐字稿玩得太開心

當我完成與 Adam Gordon Bell 的影片剪輯後,照理說就該結束了吧?我花了那麼多時間剪片,肯定會迫不及待想發布然後收工。

才怪!

影片剪完後,接著就是開始糾結逐字稿了。不過,這部分我倒是真的做得很開心。

我覺得網路上看到的每一份訪談逐字稿,設計師的想法好像都是:「我們來把 60 年前的打字機法庭筆錄,原封不動地搬到網路上,保持同樣的趣味和互動性。」

如果我們能用網頁瀏覽器讓對話逐字稿變得更有趣,會怎麼樣?

拜託!我們應該善用網路來做些打字機做不到的事吧。

所以,我先用 whisper-ctranslate2 產生初版逐字稿,然後花了很多時間讓它變得準確、具互動性又好讀:

我在訪談逐字稿中加入了一些小功能,讓閱讀更有趣。

  • 對話的每一方都以不同顏色的對話泡泡呈現,讓你一眼就能看出是誰在說話。
  • 每個對話泡泡裡都有小小的播放按鈕圖示。點擊圖示後,頁面會捲動到影片並從逐字稿對應的時間點開始播放。
  • 我把最喜歡的引言拉出來做成重點引述。
  • 我加入了標題來幫助呈現討論的架構。
  • 我校對了文字,修正轉錄錯誤。

我還沒發布這支影片,因為我週五才剛把新章節寄給電子報訂閱者。它將會在本週結束前(2025-09-12 前)上架到書籍的部落格上。

幫助 Tyler Cipriani 登上 Hacker News 第一名

有時候,計畫的發展比我期望的還要順利。

給真實的寫作者提供回饋有助於我寫這本書,所以我一直在為其他獨立開發者部落客提供自由接案的編輯服務。在介紹編輯服務的頁面上,我想放一個編輯範例,但我不想要求付費客戶把他們已經付費的作品拿來當作我的宣傳素材。

所以,我的計畫是找一位願意讓我免費編輯文章的人,條件是能公開編輯筆記,並讓對方標註我是編輯。

我的進階目標是讓這篇文章在我的書的潛在讀者會出沒的地方獲得關注,例如 Hacker News、Lobsters 和 reddit。如果讀者讀到文章最後看到「由 Refactoring English 編輯」,他們會想:「咦,這是什麼?」

對於潛在客戶來說,如果我能指著一位過去的客戶說:「你看,這個人聘請了我,他的文章就在你想成功的地方獲得了成功」,也會很有說服力。

幾個月前,Tyler Cipriani 聘請我為他的部落格做高層次的審閱。他似乎對結果很滿意,所以我向他提出了免費編輯的想法,他也答應了。

我和 Tyler 針對他的文章 「The future of large files in Git is Git」 進行了幾輪回饋。我們合作得很愉快,這也為我的書帶來了不少好點子。

我原本的額外目標只是讓文章登上 Hacker News 首頁,但結果甚至超出預期,一路衝上了 Hacker News 第一名Lobstersreddit

對我們兩人來說,最大的收穫之一就是了解針對目標讀者調整寫作的重要性。Tyler 文章的早期草稿假設讀者已經熟悉 Git LFS,一個用來管理大型檔案的 Git 擴充功能。

我建議,一般的 Git 使用者不一定對 Git LFS 了解得夠深入,足以理解文章中的所有內容。Tyler 則持不同意見,他覺得一般處理過大型檔案的 Git 使用者一定會知道 Git LFS。

為了說服 Tyler,讓他相信讀者對 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 中儲存大型檔案的方法感興趣,也會在 HN/Lobsters 上點開相關文章,但除非真的遇到需要在 git 儲存庫中存放大型檔案的情況,否則我不會主動去思考或試圖解決這個問題。

Tyler 說這份清單對他而言是「恍然大悟」的時刻。他之前一直抗拒這個回饋,因為他很有把握讀者會知道 Git LFS。看到我的清單後,他才意識到,即使讀者表面上知道 Git LFS 及其用途,也不一定了解它的運作方式。

Tyler 這個領悟的有趣之處在於,他其實自己也能寫出我的那份清單。他對目標讀者會知道什麼有著相同的預測,只是需要再深入一層去思考,讀者「知道」一項技術到底意味著什麼。

業餘專案

Hacker News Observer 切換至時間序列資料庫,速度提升 500 倍

過去幾年,我常聽到人們談論「時間序列資料庫」,但我從來不懂它們是做什麼的,或與一般的關聯式資料庫有何不同。去年我甚至為了一個業餘專案使用過 InfluxDB,因為我需要一個與 Grafana 相容的東西。但我還是不明白是什麼讓它成為「時間序列」資料庫,或為什麼不能直接用 SQLite 就好。

最近我和另一位開發者聊天,他提到使用時間序列資料庫來呈現不同視角的資料,例如秒級與天級的粒度。他甚至沒有多做解釋,但我腦中突然靈光一現,心想:「喔!原來時間序列資料庫就是做這個的!」

Hacker News Observer 是一個業餘專案,它每分鐘查詢 Hacker News,並記錄過去幾週每則貼文的按讚數、留言數和排名。我希望之後能深入挖掘、找出有趣的模式,但目前我只看了高層次的彙總資料,例如首頁的總按讚數與留言數:

切換到 DuckDB 讓這個頁面的速度提升了 500 倍

我一開始使用 SQLite 作為資料庫,上面那張圖表需要兩分鐘才能算出來。這也合理,因為每天有數千則貼文,每則貼文有數千次快照,然後我還得在每個快照中找出前 30 名(首頁),再將它們分到以小時為單位的區間中。SQLite 沒有專門用來將每分鐘資料彙整成小時視圖的特殊函式,所以需要執行大量耗時的查詢。

有了時間序列資料庫的概念後,我請 LLM 推薦類似 SQLite 的時間序列資料庫選項,它推薦了 DuckDB。接著我就讓 LLM 把我的資料庫從 SQLite 遷移到 DuckDB。光是這次遷移,就把圖表的載入時間從兩分鐘縮短到 250ms,速度提升了約 500 倍。

所以,我想這就是時間序列資料庫的用途吧。

Gleam Chat Log Parser 慢慢處理舊日誌

我在聊天日誌解析器專案上只取得了一點點進展。我處理了包含離開訊息的日誌,以及帶有 Windows 風格換行符號(\r\n的日誌。奇怪的是,在 Erlang 中(因此在 Gleam 中也是),\r\n 被算作單一字元,這讓我困惑了一陣子。

總結

完成了什麼?

經驗教訓

  • 剪輯影片時要更有紀律
  • 剪輯訪談影片很瑣碎乏味,但編輯和美化逐字稿卻很有趣。
  • 如果只需要花幾分鐘而非 30 分鐘,願意提供回饋的客戶數量會多出一個數量級。

下個月的目標

  • 發布能為《Refactoring English》網站吸引新讀者的內容。
  • 發布《Refactoring English》的新章節。
  • 寫個人化信件給 20 位未曾交流過的讀者。

本文章由 muse-spark-1.2-contributor 進行翻譯

留言