重構英文:第 13 個月
一句話總結
寫著專注,卻被分心了
初次來訪?
嗨,我是 Michael(麥可)。我是一名軟體開發者,也是小型獨立科技公司的創辦人。我目前正在撰寫一本名為 Refactoring English: Effective Writing for Software Developers(《重構英文:寫給軟體開發者的有效寫作指南》)的書。
每個月,我都會發布一篇像這樣的回顧,分享這本書以及整體工作近況的進展。
本月亮點
- 我根據 purchasing power parity(購買力平價) 為我的書新增了區域定價。
- 我打造了我的第一個 Flutter 應用程式。
- 我正在撰寫我的第一個跨語言函式庫。
目標評分
每個月月初,我都會設定想要完成的目標。以下是本月目標的達成情況:
發布一款能吸引人們造訪《重構英文》網站的遊戲
- 結果:改為發布了「2025 年 Hacker News 最受歡迎的部落格」
- 評分:B
這篇部落格文章是一場高風險的賭注,因為它只有登上 Hacker News 首頁才能觸及新讀者,而唯一的機會就是 2026 年的前幾週。
幸好,這篇文章登上了 Hacker News 第一名,並在首頁停留了將近 22 小時。這延續了我凸顯 其他 成功的科技寫作者的策略,我喜歡這個策略,因為我覺得這對我、讀者以及我所介紹的作者來說是三贏。
我的 Hacker News 預測遊戲目前已完成約 80%。我不太確定該怎麼處理它,因為它已經快完成了,但我覺得它不好玩,所以一直提不起勁把它完成。不過我還是想把它做完,看看大家的反應。
發布《重構英文》的兩個章節
- 結果:在兩個章節上取得進展,但未完成
- 評分:D
諷刺的是,我正在撰寫的章節主題正是動機與專注,但我卻一直讓 MeshCore 的實驗干擾寫作。進入新的一年後,我在維持專注方面已有改善,而這些分心其實也有幫助,因為我獲得了關於如何重拾專注的新鮮經驗可以寫進書裡。
為一個純粹好玩的家庭照片分享 App 撰寫設計文件
- 結果:完成了設計文件草稿的 80%
- 評分:C
同樣地,我在 12 月又被 MeshCore 的實驗分心,未能取得預期的進度。我喜歡設計文件,也覺得它們很有幫助,但寫起來實在非常枯燥,所以總是會想暫時擱置,轉而去做一些能帶來即時成就感的事。
《重構英文》數據指標
| 指標 | 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%) |
預售下滑是因為我沒有發布新文章來吸引新讀者(我直到 1 月才發布那篇 Hacker News 文章)。不過,我的「被動銷售」持續成長仍是個正面訊號。12 月的預售額將近 500 美元。相較於訪客人數相近的月份,5 月的預售只有 241 美元,8 月則是 361 美元,可見數字呈現上升趨勢。我希望隨著書籍內容越來越完整、越來越多讀者推薦,被動銷售能持續成長,而不必仰賴我每個月都要找到一次成功的行銷推廣。
為我的書新增區域定價
當我在 11 月舉辦 Black Friday 促銷時,一位讀者來信表示,即使打了七折(20 美元),在阿根廷對一本書來說仍然是難以負擔的價格。他詢問我是否會考慮區域定價。他提到 Steam 遊戲在阿根廷的售價通常比美國低 50%,所以我覺得這是個不錯的參考基準。
我透過 Stripe 收款,但在 Stripe 後台找不到任何區域定價的選項。我在 Stripe 的知識庫中找到一篇名為「Geographic pricing in practice: Why it matters and how to implement it.」的文章。我本來很開心,直到把整篇文章讀完,才發現他們忘了寫「以及如何實作」的部分。
因此,Stripe 雖然提倡區域定價,卻實際上並未提供這項功能。這也很有幫助地提醒了我,Stripe 是最爛的金流處理商——除了其他所有金流處理商之外。
因此,對於這位阿根廷客戶,我採用了一次性的做法,手動為他建立了一個折扣價的自訂付款連結。在操作過程中,我發現我可以將價格設定為阿根廷披索,這樣他就不用支付貨幣轉換費。我將價格設為 22,000 ARS(約 15 美元),他似乎對這個價格和結帳體驗感到滿意。
這位讀者建議我公開提供區域定價,至少針對像巴西和印度這樣開發者人數眾多但購買力相對較低的國家。
即使 Stripe 原生不支援區域定價,將我手動操作的事情自動化似乎也不會太難。我讀到 Sebastien Castiel(塞巴斯蒂安·卡斯蒂爾)為他的課程實作區域定價的經驗,進而找到了 Wes Bos(威斯·柏斯)關於同一主題的文章。
塞巴斯蒂安分享了許多技術細節,但他的解法大量依賴 React,而我的網站則是原生的 HTML 和 JavaScript。他也依賴折扣碼,這點我不太喜歡,因為這會讓多數顧客看到有一個他們無法享有的特別優惠。
我花了幾個小時用 cloud function 實作了一個解法,它能即時判斷合適的價格並動態產生 Stripe 結帳連結。接著,我意識到可以預先計算好所有價格,從而省去伺服器端邏輯,於是我把那個 cloud function 刪掉了。
我的實作方式如下:
- 手動取得 Stripe 支援的所有國家/貨幣清單。
- 撰寫一個腳本,從世界銀行提取資料來計算清單中每個國家的purchasing power parity(PPP)。
- 根據每個國家相對於美國的購買力計算其折扣。
- 例如,巴西的 PPP 比美國低 54%,因此可獲得 54% 的折扣。
- 過濾掉 PPP 與美國差距在 15% 以內的國家(折扣太小,不值得處理)。
- 過濾掉折扣會變成負數的國家。
- 否則,盧森堡的顧客就得付兩倍的價格。
- 將折扣上限設為 75%
- 否則埃及的價格會變成 4 美元,代表扣除轉換手續費後我大概只能拿到 3.50 美元。
- 為清單中剩下的每個國家自動產生專屬的 Stripe 價格物件和 Stripe 付款連結。
- 將所有國家放入我網站上的 HTML 下拉式選單中:
使用者只需選擇自己的國家,就會啟用該國家的 Stripe 購買連結,並以當地貨幣付款。
我採用誠信制,因此不處理 IP 地理定位或 VPN 防範。我會隱藏每個國家的折扣幅度,以避免人們刻意選擇最便宜的選項。而以各國當地貨幣定價的好處之一是,如果有人作弊、選擇並非自己所在地的區域,他們會在貨幣轉換費上損失一些錢。
這些數字感覺不太準確。根據嚴格的 PPP 計算,在美國賣 30 美元的商品在埃及等值於 4 美元,但我懷疑在埃及是否真的能用 4 美元買到非盜版的程式設計書籍。
威斯·柏斯當時是直接請讀者告訴他合理的價格,所以我也來試試看。歡迎留言或來信告訴我,在你的國家一本像 Designing Data-Intensive Applications 這類針對開發者的書籍,通常的價格範圍是多少(以當地貨幣計)。
打造我的第一個 Flutter 應用程式
12 月時,我發布了「我對 MeshCore 離網訊息傳遞的第一印象」。我對這項技術感到興奮,但發現客戶端全都是封閉原始碼時感到很失望。
在那個時候,我決定暫停對 MeshCore 的探索,但 MeshCore 貢獻者 Frieder Schrempf(弗里德·施倫普夫)回覆了我的文章,並提出了這個有趣的觀點:
在這個主題上,我與你的許多想法一致。就我個人而言,我認為 MeshCore 的價值在於協定本身,而不在於韌體、應用程式等軟體實作。[……] 如果 MeshCore 作為一種協定能夠成功並被廣泛採用(目前看起來確實如此),那麼維護良好的開源實作自然會隨之出現(至少我是這麼希望的)。
我認同弗里德的看法,心想:「或許我應該直接寫一個開源 MeshCore 應用程式的概念驗證?」
其實,已經有一個 MeshCore 應用程式的概念驗證了。官方 MeshCore 應用程式的開發者 Liam Cottle(連恩·科特爾)先前曾為 MeshCore 寫過一個網頁應用程式,作為官方版的原型。當他製作官方(專有)的 MeshCore 應用程式時,就棄用了這個原型,但其原型的原始碼仍然可以取得,而且該原型已具備我需要的大部分功能。
我好奇將這個原型移植到行動裝置會有多困難。MeshCore 作為網頁應用程式太難用了,因為它需要藍牙存取和離線模式。我聽過一些對 Flutter(Google 的跨平台行動開發解決方案)還算正面的評價。我猜想 LLM 應該能在不需要我太多介入的情況下,成功將網頁原型的程式碼移植到 Flutter。
我的計畫是讓 LLM 分三個階段將原型移植到 Flutter:
- 使用 Playwright 為原型網頁應用程式撰寫端對端測試。
- 將原型實作移植到 Flutter 網頁應用程式,並保持端對端測試不變以確保功能一致。
- 為 Flutter 專案新增 Android 建置。
這個方法奏效了,但每個步驟都比我預期的還要笨拙:
- 在我能為原型撰寫端對端測試之前,我必須先將其轉換為使用語意化 HTML 和 ARIA 屬性,因為許多輸入標籤只是裸露的
<div>。 - 我無法保持 Playwright 測試不變,因為 Flutter 實際上不會為網頁應用程式產生語意化 HTML。它會建立自己專屬的 Flutter 方言 HTML,並將所有內容繪製在 HTML canvas 上。大多數 Playwright 的元素定位器不知為何仍能運作,但我必須對測試做出許多針對 Flutter 的修改。
- 即使有 LLM 協助,要搞清楚如何用 Flutter 建置 Android 套件也花了很長時間。
- Gradle,也就是 Android 的建置系統,在 NixOS 上有不少錯誤。我不斷遇到它以神祕錯誤失敗的情況,最後才發現是它快取在家目錄中的陳舊資料所致。
- Flutter 在透過藍牙通訊方面出乎意料地困難。在網頁上(至少在 Chrome 上),你基本上只要呼叫
navigator.bluetooth.requestDevice就能免費獲得這個功能,但在 Flutter 上,你必須使用一個專有的第三方函式庫,並自行打造裝置選擇器的介面。
我原以為這會是個能在幾小時內快速完成的週末小專案。結果花了 30 小時和 200 美元的 LLM 額度後,我才終於讓它動起來。

在實體 Android 裝置上執行我的 MeshCore Flutter 應用程式
但就在我的 Flutter 實作達到與原型功能一致的那天,我到 Reddit 上準備分享時,看到有人剛分享了 meshcore-open,一個用 Flutter 實作的 MeshCore 客戶端。這跟我的想法一模一樣,但執行得好太多了。
雖然被人搶先一步讓我有點失望,但我也鬆了一口氣。從我短暫使用 Flutter 的經驗來看,我迫不及待想盡快遠離 Flutter。我本來就只想做一個概念驗證,希望有其他人能接手,所以現在看到有一個開源且功能豐富的 MeshCore 客戶端實作,我很開心。
或許 MeshCore 需要的是一個跨語言函式庫
在開發我的 MeshCore Flutter 應用程式時,我必須實作底層邏輯來解析 MeshCore 裝置對客戶端的訊息。有一份公開的 spec 定義了 MeshCore 的點對點協定,而且就連那份文件也相當寬鬆。但還有另一種未公開文件的協定,用於描述運行 MeshCore 韌體的裝置如何透過藍牙或 USB 與 companion client(例如 Android 應用程式)通訊。
事實上的參考實作是 MeshCore 韌體,但它將點對點協定邏輯與裝置對客戶端協定邏輯以及 UI 邏輯混雜在一起,並將實作分散在程式碼庫中不同的地方。
舉例來說,MeshCore 客戶端可以透過藍牙從 MeshCore 裝置取得聯絡人清單,但它必須將原始位元組反序列化回聯絡人。沒有用於解碼訊息的函式庫,因此每個 MeshCore 客戶端和函式庫都在各自實作自己的版本:
關於這些實作,我注意到以下幾點:
- 它們必須使用像
32這樣的 magic number,而不是參照定義在某個權威位置的常數。 - 它們都沒有為解析器撰寫自動化測試。
- 它們將不必要的底層工作帶進了高階語言。舉例來說,每個人都在儲存
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 尚未能編譯至 xtensa 架構,而大多數 MeshCore 裝置使用的是這種架構。
- 大多數 MeshCore 韌體專案使用的 PlatformIO 不支援 Zig。
- Dart 的 ffigen 或許能與 Zig 搭配使用,因為 Zig 支援 C 的 ABI,但即使要讓它與 C 一起運作就已經很困難了。
- Python 的 cffi 也是如此。
總結
完成了什麼?
- 我大致完成了《重構英文》兩個新章節的撰寫。
- 我大致完成了設計文件的撰寫,這是為我的照片分享應用程式點子所寫的。
- 我發布了「The Most Popular Blogs of Hacker News in 2025」。
- 我打造了我的第一個 Flutter 應用程式。
- 我打造了我的第一個跨語言函式庫。
- 我做了一些貢獻 給 MeshCore meshcore.js。
- 其中大部分都被維護者忽略了。
經驗教訓
- 盡量減少同時進行中的專案
- AI 讓啟動新專案變得比以往任何時候都更容易,但要將它們轉化為可正式發布的產品,瓶頸仍然是我。結果就是我手上有大量進行中的專案,都在等我審閱後才能發布。如此頻繁的上下文切換和任務追蹤帶來了不小的心理負擔。
下個月的目標
- 發布《重構英文》的三個章節。
- 發布我的 2025年度回顧(第 8 年)。
尋求協助
在你居住的地方,一本針對開發者的書售價 30 美元(USD)算貴嗎?如果是的話,請告訴我,在你的國家一本像 Designing Data-Intensive Applications 這樣的程式設計書籍,你預期會付多少錢(以當地貨幣計)。
隨機一篇部落格
