Refactoring English:第 13 個月
原文由 Michael Lynch 于 發布,訂閱此部落格
一句話總結
在寫專注的同時卻一直分心
第一次來?
嗨,我是 Michael。我是一名軟體開發者,也是幾間小型獨立科技公司的創辦人。目前我正在寫一本書,書名是Refactoring English: Effective Writing for Software Developers。
每個月我都會像這樣發表一篇回顧,分享這本書以及整體工作近況的進展。
本月亮點
- 我根據購買力平價,為我的書新增了區域定價。
- 我做了第一個 Flutter App。
- 我正在寫第一個跨語言函式庫。
目標成績單
每個月月初,我都會宣告當月想完成的目標。以下是這個月的達成狀況:
發布一款能吸引大家造訪 Refactoring English 網站的遊戲
- 結果:改為發表了〈The Most Popular Blogs of Hacker News in 2025〉
- 評分:B
這篇部落格文章是一場冒險,因為只有登上 Hacker News 首頁才有機會觸及新讀者,而唯一的機會就是 2026 年的前幾週。
幸運的是,這篇文章衝上了 Hacker News 第一名,並在首頁停留了將近 22 小時。這延續了我介紹 其他 成功科技作家的策略,我喜歡這個策略,因為對我、讀者以及被我介紹的作家來說,都是三贏。
我的 Hacker News 預測遊戲目前大概完成了 80%。我不太確定該怎麼處理它,因為雖然快完成了,但我覺得它不好玩,所以一直提不起勁把它做完。不過我還是想把它完成,看看大家的反應。
發表 Refactoring English 的兩個章節
- 結果:兩個章節都有進展,但都沒完成
- 評分:D
諷刺的是,我正在寫的章節主題正是動機與專注,但我卻一直讓 MeshCore 的實驗干擾寫作。進入新的一年後,我在維持專注方面有進步了,而且這些分心其實也有幫助,因為讓我有了重新找回專注的最新體驗可以寫進書裡。
為一個純好玩的家庭照片分享 App 撰寫設計文件
- 結果:設計文件草稿完成了 80%
- 評分:C
同樣地,12 月我又被 MeshCore 的實驗分心了,沒有達到預期的進度。我很喜歡設計文件,也覺得很有用,但寫起來真的非常無聊,所以總是會想把它先擱著,去做一些能更快獲得成就感的事。
Refactoring English 數據
| 指標 | 2025 年 11 月 | 2025 年 12 月 | 變化 |
|---|---|---|---|
| 不重複訪客 | 7,608 | 2,266 | -5,342 (-70%) |
| 預購收入 | $1,018.48 | $492.55 | -$525.93 (-52%) |
| 贊助收入 | $48.25 | $48.25 | $0.00 (0%) |
| 總收入 | $1,066.73 | $540.80 | -$525.93 (-49%) |
預售下滑是因為我沒有發表新文章來吸引新讀者(Hacker News 那篇文章要到一月才發)。不過,「被動銷售」持續成長是個好兆頭。12 月的預售額將近 500 美元。跟訪客人數相近的月份相比,5 月的預售只有 241 美元,8 月則是 361 美元,所以數字是呈上升趨勢的。我希望隨著書的內容越來越完整、也有更多讀者推薦,被動銷售能持續成長,而不必每個月都靠我找到成功的行銷操作。
為我的書新增區域定價
11 月做黑色星期五促銷時,有位讀者來信說,即使打了七折(20 美元),在阿根廷對一本書來說還是太貴。他問我是否考慮區域定價。他提到 Steam 遊戲在阿根廷的售價通常比美國低 50%,我想這是個不錯的參考基準。
我透過 Stripe 收款,但在 Stripe 後台找不到任何區域定價的選項。我在 Stripe 知識庫找到一篇文章,標題是〈Geographic pricing in practice: Why it matters and how to implement it〉。我本來很開心,直到把整篇文章看完才發現,他們根本忘了寫「and how to implement it」那個部分。
所以,Stripe 雖然提倡區域定價,但實際上根本沒提供這個功能。這再次提醒了我,Stripe 是最爛的金流處理商——除了其他所有金流處理商之外。
於是,針對這位阿根廷的顧客,我用了一個一次性的做法,手動為他建立了一個折扣價的專屬付款連結。在操作過程中,我發現可以直接用阿根廷披索定價,這樣他就不用付貨幣轉換費。我把價格設為 22,000 ARS(約 15 美元),他似乎對價格和結帳體驗都很滿意。
這位讀者建議我公開提供區域定價,至少針對像巴西和印度這樣開發者眾多但購買力相對較低的國家。
即使 Stripe 沒有原生支援區域定價,要把我手動做的流程自動化,似乎也不會太難。我讀到 Sebastien Castiel 為他的課程實作區域定價的文章,也因此看到了 Wes Bos 寫的相關文章。
Sebastien 分享了很多技術細節,但他的解法很依賴 React,而我的網站只有原生 HTML 和 JavaScript。他也用了折扣碼,這點我不太喜歡,因為這會讓大多數顧客看到有一個自己拿不到的優惠。
我花了幾個小時用雲端函式實作了一個解法,可以即時判斷正確價格並動態產生 Stripe 結帳連結。後來我發現其實可以預先把所有東西都算好,就不需要伺服器端邏輯了,所以就把雲端函式刪掉了。
我的實作方式如下:
- 手動取得 Stripe 支援的所有國家/幣別清單。
- 寫一個腳本,從世界銀行抓資料來計算清單上每個國家的購買力平價(PPP)。
- 根據每個國家相對於美國的購買力來計算折扣。
- 例如,巴西的 PPP 比美國低 54%,所以就給 54% 的折扣。
- 排除 PPP 與美國差距在 15% 以內的國家(折扣太小,不值得處理)。
- 排除折扣會是負值的國家。
- 不然盧森堡的顧客就得付兩倍價錢了。
- 將折扣上限設為 75%
- 不然埃及的價格會變成 4 美元,扣掉轉換費後我大概只會拿到 3.5 美元。
- 為清單上剩下的每個國家自動產生專屬的 Stripe 價格物件和付款連結。
- 把所有國家放進網站上的一個 HTML 下拉選單中:
使用者只要選擇自己的國家,就會啟用該國專屬的 Stripe 購買連結,並以自己的貨幣付款。
我採取榮譽制,所以不會去做 IP 定位或防 VPN。我會把每個國家的折扣藏起來,以避免大家都去選最便宜的選項。而用各國當地貨幣定價的好處之一是,如果有人作弊選了不是自己所在地的區域,還會因為轉換費而多付一些錢。
這些數字感覺不完全正確。按照嚴格的 PPP 計算,在美國賣 30 美元的東西,在埃及等於 4 美元,但我懷疑在埃及真的沒辦法用 4 美元買到給程式設計師看的正版書。
當初 Wes Bos 就是直接請讀者告訴他合理的價格,所以我也來試試看。歡迎留言或寫信給我,告訴我在你的國家,一本給開發者看的書平常價格範圍大概是多少(用當地貨幣)。
打造我的第一個 Flutter App
12 月時,我發表了〈My First Impressions of MeshCore Off-Grid Messaging〉。我對這項技術感到很興奮,但發現所有客戶端都是封閉原始碼時有點失望。
當時我決定先暫停對 MeshCore 的探索,但 MeshCore 的貢獻者 Frieder Schrempf 在我的文章下回覆了這個有趣的觀點:
我在這個議題上有很多想法跟你一樣。就我個人而言,我認為 MeshCore 的價值在於協定本身,而比較不在於韌體、App 等軟體實作。[⋯⋯] 如果 MeshCore 這個協定能成功並被廣泛使用(目前看起來確實如此),之後就會出現維護良好的開源實作(至少我是這麼希望的)。
我認同 Frieder 的看法,心想:「或許我該自己寫一個開源 MeshCore App 的概念驗證?」
其實,已經有一個 MeshCore 的概念驗證 App 了。官方 MeshCore App 的開發者 Liam Cottle 之前曾為 MeshCore 寫過一個網頁 App,作為正式版的原型。他在推出官方(專有)的 MeshCore App 後就棄用了這個原型,但原型的原始碼還在,而且已經具備我需要的大多數功能。
我在想,把這個原型移植到行動裝置會有多難。MeshCore 作為網頁 App 太難用了,因為它需要藍牙存取和離線模式。我聽過一些對 Flutter 的正面評價,這是 Google 推出的跨平台行動開發解決方案。我猜想,靠 LLM 應該就能在不太需要我介入的情況下,成功把網頁原型的程式碼移植到 Flutter 上。
我的計畫是讓 LLM 分三個階段把原型移植到 Flutter:
- 使用 Playwright 為原型網頁 App 撰寫端對端測試。
- 將原型的實作移植成 Flutter 網頁 App,保持端對端測試不變以確保功能一致。
- 為 Flutter 專案加入 Android 建置。
這個方法可行,但每一步都比我預期的還要卡:
- 在為原型寫端對端測試之前,我得先把它改成使用語意化 HTML 和 ARIA 屬性,因為很多輸入框的標籤只是光禿禿的
<div>。 - 我沒辦法讓 Playwright 測試保持不變,因為 Flutter 的網頁 App 實際上不會產生語意化 HTML。它會產生自己專屬的 Flutter HTML 方言,並把所有東西都畫在 HTML canvas 上。雖然大多數 Playwright 的元素定位器還是能勉強運作,但我還是得對測試做很多針對 Flutter 的修改。
- 即使有 LLM,要搞清楚如何用 Flutter 建置 Android 套件也花了很久。
- Gradle,也就是 Android 的建置系統,在 NixOS 上有很多 bug。我一直遇到神秘的錯誤,最後才發現都是它快取在家目錄裡的過期資料搞的鬼。
- Flutter 在藍牙通訊上意外地很難用。在網頁上(至少在 Chrome 上),只要呼叫
navigator.bluetooth.requestDevice就幾乎能免費取得,但在 Flutter 上,你得使用一個專有的第三方函式庫,還得自己刻裝置選擇器的 UI。
我本來以為這會是個幾小時就能搞定的週末小專案。結果花了 30 小時和 200 美元的 LLM 點數後,才終於讓它動起來。

在實體 Android 裝置上執行我的 MeshCore Flutter App
但就在我的 Flutter 實作終於做到跟原型功能一致的那一天,我到 Reddit 上想分享時,才看到有人剛分享了 meshcore-open,一個用 Flutter 寫的 MeshCore 客戶端。想法跟我一模一樣,但做得好太多了。
有人搶先一步讓我有點失望,但也鬆了一口氣。經過這次短暫的 Flutter 開發經驗,我巴不得越快遠離 Flutter 越好。我本來就只想做個概念驗證,希望有人能接手,所以現在看到有個開源、功能豐富的 MeshCore 客戶端實作,我很開心。
或許 MeshCore 需要的是跨語言函式庫
在開發我的 MeshCore Flutter App 時,我得實作底層邏輯來解析 MeshCore 裝置對客戶端的訊息。有個公開的規格定義了 MeshCore 的點對點協定,而且定義得相當寬鬆。但還有另一個未公開的協定,是用來描述執行 MeshCore 韌體的裝置如何透過藍牙或 USB 與 companion 客戶端(例如 Android App)通訊的。
事實上的參考實作就是 MeshCore 韌體本身,但它把點對點協定邏輯、裝置對客戶端協定邏輯和 UI 邏輯全混在一起,而且實作散落在程式碼庫的各個角落。
舉例來說,MeshCore 客戶端可以透過藍牙從 MeshCore 裝置取得聯絡人清單,但必須把原始位元組反序列化回聯絡人。沒有現成的函式庫可以解碼訊息,所以每個 MeshCore 客戶端和函式庫都在各自重做一套:
我對這些實作的觀察是:
- 它們得使用像
32這樣的魔術數字,而不是參考某個權威位置所定義的常數。 - 沒有任何一個有針對解析器的自動化測試。
- 它們把不必要的底層工作帶進了高階語言。例如,大家都存了
outPath和outPathLen兩個變數。這是 C 語言實作的遺留物,因為在 C 裡陣列不知道自己的大小。但在 JavaScript、Python 或 Dart 這類語言裡,根本不需要手動追蹤陣列大小。 - 它們沒有仔細檢查資料,所以會開心地把像負的路徑長度或超出地球範圍的 GPS 座標這類垃圾資料直接傳下去。
- 它們全都忽略了 flags 欄位,儘管 flags 本來應該用來表示哪些欄位有值。至少在點對點訊息中應該是如此。對於裝置對客戶端的訊息來說,flags 似乎毫無意義。
我一開始想用 protobuf 或 Cap’n Proto 這類協定函式庫來重寫邏輯,但目前看不到能向後相容地整合第三方函式庫的方法。
那如果我用 C 來寫 MeshCore 裝置對客戶端協定的核心實作呢?我可以加上各種語言的綁定,這樣就不需要為 Dart、Python、JavaScript 和其他你想用的語言各自重做整套實作了。
所以,我開始了自己的 MeshCore 客戶端函式庫:
這個函式庫還沒準備好可以當作概念驗證來展示,但已經很接近了。
MeshCore 的維護者很有可能不喜歡這個想法,沒有他們的支持,這個計畫基本上就寸步難行。但我還是做了,因為我從沒試過寫跨語言函式庫,這是個有趣的經驗。
上一次我嘗試從 Python 呼叫 C 程式碼是 20 年前,那時得用 SWIG。當時感覺很痛苦又很土炮,現在似乎已經改善了八成。
我非常想用 Zig 而不是 C 來寫核心實作,但遇到了太多阻礙:
- Zig 還無法編譯到大多數 MeshCore 裝置使用的 xtensa 架構。
- 大多數 MeshCore 韌體專案使用的 PlatformIO 也不支援 Zig。
- Dart 的 ffigen 或許能搭配 Zig 使用,因為 Zig 支援 C 的 ABI,但光是要讓它跟 C 搭配就已經很難了。
- Python 的 cffi 也是同樣的情況。
總結
完成了什麼?
- 《Refactoring English》有兩個新章節已經寫了大半。
- 我的照片分享 App 構想的設計文件也寫了大半。
- 發表了〈The Most Popular Blogs of Hacker News in 2025〉。
- 做了我的第一個 Flutter App。
- 做了我的第一個跨語言函式庫。
- 對 MeshCore 的 貢獻 了 meshcore.js 一些。
- 其中大部分,維護者都還沒理會。
經驗收穫
- 盡量減少同時進行的專案
- AI 讓開新專案變得前所未有的容易,但要把它們變成可上線的成品,瓶頸還是在我自己。結果就是手上一堆進行中的專案,都在等我審閱後才能發表。頻繁的情境切換和任務追蹤帶來了很大的心智負擔。
下個月的目標
- 發表《Refactoring English》的三個章節。
- 發表我的 2025 年度回顧(第 8 年)。
需要協助的地方
在你住的地方,一本給開發者看的書賣 30 美元算貴嗎?如果是的話,請告訴我你在你的國家,會預期為一本像 Designing Data-Intensive Applications 這樣的程式設計書籍付多少錢(以當地貨幣計算)。
隨機一篇部落格

留言
登入後參與討論