Refactoring English: Month 18

Michael Lynch

重構英文:第 18 個月

一句話總結

這本書完成了!嗯,算是吧。

第一次來嗎?

嗨,我是 Michael(麥可)。我是一名軟體開發者,也是小型獨立科技事業的創辦人。我目前正在撰寫一本名為 Refactoring English: Effective Writing for Software Developers(《重構英文:軟體開發者的高效寫作指南》) 的書。

每個月,我都會發表像這樣的回顧,分享我的書與整體職涯的近況。

重點回顧

  • 我已完成全書 22 章。
  • 我本以為 AI 讓原型開發更快,但現在不太確定了。

目標成績

每個月月初,我會宣告當月想完成的目標。以下是我的達成狀況:

讓《重構英文》達到「內容完稿」

  • 結果:已完成所有章節。
  • 成績:A

過去六週以來,一直感覺再一週就能完成,所以終於把所有章節都寫完,真的鬆了一口氣。

打造讓《重構英文》讀者在閱讀時能提供回饋的工具

  • 結果:工具僅完成約 40%。
  • 成績:C

這原本看起來應該是個兩、三天就能做完的專案,但實際上比想像中困難,尤其是受到 the great blockade(大阻塞) 的影響。

《重構英文》數據

指標2026 年 4 月2026 年 5 月變化
不重複訪客2,5781,752-826 (-32%)
預購收入$587.73$407.61-$180.12 (-31%)

哎呀,我持續忽略行銷,數據也因此下滑。

我急著想把書的最後幾章寫完,所以只專注於此,完全沒有投入任何行銷。

漏洞獎金數據

我仍持續投入資安漏洞獎金計畫,但已減少投入的時間。雖然原本計畫是 70/30 的時間分配,但實際上大概是 60/40。

我主要合作的廠商又支付了 7,000 美元(累計達 17,000 美元)的漏洞回報獎金,不過他們處理回報的速度變慢了,所以我大多已停止在他們的程式碼中尋找新漏洞。

我向其他幾個專案提交了漏洞,想看看是否有能快速處理回報的,但目前都沒有:

  • KeePassXC - 我在 5 月 18 日透過 Zero Day Initiative 提交了一個 RCE(遠端程式碼執行) 漏洞,但尚未收到任何回應。
    • 對於 KeePassXC 的使用者來說,這並非零點擊攻擊,也不會因為瀏覽惡意網站就導致資料庫被入侵,所以不用太擔心。
  • Cloudflare - 我在 5 月 22 日透過 HackerOne 提交了一個 DoS(阻斷服務)/邏輯繞過漏洞。尚未收到回應。
  • Proton - 我提交了一個低嚴重性的問題。對方要求提供影片形式的 PoC(概念驗證),所以我在 5 月 29 日製作並提交後,對方表示會再另行通知。

這本書何時才算「完成」?

我已完成書中所有章節,這讓人鬆了一口氣,但我並不認為它已正式「完成」。

過去一年半以來,我通常一次專注於一個章節來撰寫這本書。我從未從頭到尾完整讀過自己的書,以確認整體的一致性。在宣告完成之前,我想至少完整通讀幾遍。

為什麼我沒有持續修訂這本書?

我原本計畫根據讀者回饋持續修訂這本書。這樣一來,等寫到最後一章時,書本應該就差不多完成了,因為前面章節已經過多次修訂,整合了讀者的意見。

實際上,我整合讀者回饋的程度遠低於預期。

我發現很難同時兼顧修訂舊章節與撰寫新章節。如果花一週時間修訂舊章節,就感覺沒有向前推進;而新增一章時,我的公開進度條就會前進一些,這很有激勵效果。

來自書籍網站的進度條

我沒有持續修訂的另一個原因是,我向讀者徵求意見的頻率不如原先計畫。部分原因是,我一直覺得書的進度落後,所以總想著:「等這一章寫完,再來投入讀者聯繫。」

但即使我聯繫了讀者,也很少對書本產生實質影響。最常見的回應是:「我喜歡這本書」或「我還沒開始看」。

即使收到詳細的回饋,我也不總是知道該如何整合。在某些情況下,我認同回饋意見,處理起來就很簡單。但通常讀者會建議加入一些我認為不需要的內容。這並非表示讀者錯了,但在違背自己直覺之前,我會想看到回饋中出現某種規律,而我收到的回饋數量並不足以看出規律。

我的讀者回饋工具

既然所有章節都已完成,我覺得有更多餘裕去接觸讀者了。

我很喜歡 Help this Book 這個構想,這是一款讓讀者能直接在電子書中提供回饋的網頁應用程式,但我不想把所有回饋都交給第三方託管並按月付費。

我看到 Julia Evans(茱莉亞・埃文斯)為自己的產品打造了專屬的讀者回饋工具,覺得很不錯,所以我也正在做類似的東西。

我正在開發一個網頁應用程式,讓讀者更容易針對我的書提供回饋。

AI 專案與大阻塞

整體而言,我發現 AI 確實讓我在寫程式時更有生產力。在處理像是解決 Git 合併衝突、為不熟悉的程式碼除錯,或製作簡單工具等特定任務時,AI 的幫助非常明顯。

我過去認為 AI 在協助啟動專案方面表現優異,但現在不太確定了。我不斷撞上我所稱的「the great blockade」。

直接讓 AI 做原型

六個月前,我會給 AI 代理一個高層次的概述,請它實作一個基本的 v1 版本。我知道代理產出的內容會很混亂,但那只是原型,所以我可以持續給予回饋,直到它符合我的程式設計品味。

事實證明,清理一個糟糕的原型比我想像中困難得多。一旦原型夠糟,我就很難釐清程式碼到底想做什麼。

AI 似乎有一種奇怪的偏見,會為已經存在的程式碼辯護。如果我告訴 AI 某個元件令人困惑,因為它對同一筆資料迭代了三次,它只會不斷堅持必須迭代三次,理由是 X、Y 和 Z。但它從來不會質疑 X、Y 和 Z 是否是人為的限制。

這就是阻塞點。我被困在試圖跨越 AI 建構的巨大混亂程式碼高牆之外。

如果不修正核心邏輯,問題會持續惡化。程式碼異味會像黴菌一樣滋生並蔓延至整個程式碼庫。我在脆弱的基礎上繼續建構,而 AI 只是不斷複製已存在的糟糕模式。

拆解原型

好吧,簡單的解法:讓 AI 代理分小塊來建立原型。把 AI 的韁繩勒緊一點,別讓它偏離太遠。與其讓 AI 一次建立整個原型,不如先從歡迎頁面開始。審核並合併後,再加入一個簡單的功能,依此類推。

這在遇到像身分驗證這類複雜區塊之前都運作良好。AI 會產生一個 2k 至 5k LOC、令人困惑的拉取請求,形成一堵巨大的高牆。我想不到任何方法能再進一步拆解這個功能,所以又卡在這個巨大的 PR 上,也就是另一個大阻塞。

不僅 4k LOC 的變更需要花費 20 倍於 400 LOC 變更的審查時間,還需要更大的審查時間區塊。如果我有 20 分鐘的空檔,可以處理 400 LOC 的變更,但如果是 4k LOC 的變更,光是建立上下文就需要 20 分鐘。若想在不把大部分時間浪費在上下文切換的摩擦上的情況下,對 4k LOC 的變更取得實質進展,我需要 90 分鐘的完整時段,這對於週末專案來說尤其難得。

範例:為 Little Moments 實作身分驗證

舉個例子。對於 Little Moments,我採用 透過魔術登入郵件進行身分驗證 的方式。好幾個星期以來,我都想不到如何在不引入無用程式碼或損壞功能的情況下拆解這個功能。我無法只實作一半的登入流程。

在連續數週一點一點地處理這個巨大 PR 後,我才意識到其實可以只實作半套登入流程。PicoShare 是我維護的另一個應用程式,它有一套簡單的身分驗證流程。該應用程式假設只有單一授權使用者,因此驗證只需要一組通關密語,甚至不需要帳號/密碼組合。與其從無驗證直接大幅切換到電子郵件驗證,我可以先從無驗證進展到通關密語驗證。

於是,我讓通關密語驗證運作起來了,但從通關密語轉移到魔術郵件登入仍然是一個相當龐大的 PR,需要數週才能審查完。經過數天的嘗試後,我意識到還可以進一步拆解。

與其實際寄送帶有登入連結的電子郵件,我可以直接將使用者重新導向到我原本會寄送的連結。這仍然是一個 1.7k LOC 的 PR,但比起實際寄送郵件的版本更易於管理。也讓實際寄送郵件的部分縮減到僅 1k LOC。

AI 如何讓這件事變得更困難

讓我懷疑 AI 在這類工作上是否真能帶來淨效益的原因在於,我知道如果沒有使用 AI,我早就會發現這些拆解問題的機會。我絕不會建立一個 4k LOC 的 PR 然後說:「嗯,這有點大。」隨著 PR 越變越大,處理起來會越痛苦,因此我自然會看到將變更拆成更小部分的機會。

AI 打亂了這種自然的回饋循環。使用 AI 時,建立一個 4k LOC 的 PR 毫無痛苦,因為它在兩分鐘內就完成了,而我只是在查看電子郵件。我可以輕鬆地針對這個 4k LOC 的 PR 提出改進意見並感覺自己有所進展,但巨大的變更讓我很難辨識出哪些部分可以抽出來成為更小的獨立變更。

既然我已意識到為複雜變更產生巨大且難以管理的 PR 是多麼容易,我就能改變使用 AI 的方式,在前期投入更多心力,將功能拆解成更細小的變更。

總結

完成了什麼?

  • 發布了本書的最後幾個章節。
  • 為書籍回饋應用程式建立了部分原型。
  • 為 Little Moments 部分實作了身分驗證。
  • 為 PicoShare 發布了兩個新版本。

經驗教訓

  • 使用 AI 消除了促使我以更小單位建構軟體的自然回饋循環。
    • 我認為解法是在複雜功能的生命週期早期更努力地將其拆解為更小的區塊,並更嚴格地檢查 AI 的輸出。

下個月的目標

  • 投入至少五小時改善《重構英文》網站。
  • 為《重構英文》網站吸引 30k 名不重複訪客。
  • 完成我的讀者回饋工具。

原文由 Michael Lynch 發布

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