TinyPilot:第 38 個月
一句話總結
我對軟體的投資方向正確嗎?
第一次來嗎?
嗨,我是 Michael(麥可)。我是軟體開發者,也是 TinyPilot 的創辦人,TinyPilot 是一家獨立的電腦硬體公司。我在 2020 年創立這家公司,現在每月營收約 6 萬至 8 萬美元,並聘雇了另外七位員工。
每個月,我都會發表一篇像這樣的回顧,分享我的事業與整體職涯近況。
本月亮點
- 我未能成功銷售 TinyPilot 授權的定期訂閱制。
- 我發現自己把 TinyPilot 做得過度可設定了。
- 我原本以為自己對 TinyPilot 開發的投資不當,但寫這篇回顧時才發現,其實大致上方向是正確的。
目標達成度評分
每個月月初,我都會宣告想完成的事。以下是我達成這些目標的情況:
盡快將製造轉移至委外製造商
- 結果:我很快就讓製造商不再卡關,但錯失了加速進度的機會。
- 評分:B-
每當委外製造商因為需要我的回饋而卡關時,我都優先提供快速且完整的回覆,我覺得這方面做得不錯。
但我太晚才意識到,我應該更主動地管理專案。製造商那邊有專案經理,所以我以為他們會把事情盯好,但最終,如果進度延誤,損失最大的還是我。
當他們針對包裝盒設計或說明手冊等事項徵求回饋時,我會迅速回覆,然後就拋諸腦後,直到對方主動追蹤才又想起。直到他們準備寄出最終樣品時,我才發現自從提供回饋後,我就再也沒看過包裝盒或說明手冊的最終定稿。結果設計還需要更多修改,導致時程不必要地延誤。
制定搬離 TinyPilot 實體辦公室的詳細計畫
- 結果:我們現在已有一份按月規劃、包含目標日期與里程碑的搬遷計畫。
- 評分:A
我們已經有了計畫,大家對時程也都有共識。
有些設備的處理還有先有雞還是先有蛋的問題。像是如果我們把印表機賣掉了,要怎麼列印寄送標籤來賣其他東西?不過最壞的情況,就是我把剩下的東西先搬回家,從家裡繼續賣。
測試 TinyPilot 授權自動續訂的選項
- 結果:我評估了幾個選項,但沒有一個好用。
- 評分:B
我本來希望能找到一個 Shopify 應用程式,讓我可以銷售 TinyPilot Pro 的定期訂閱,但一個也找不到。更多細節請見下文。
TinyPilot 數據統計
| 指標 | 2023 年 7 月 | 2023 年 8 月 | 變動 |
|---|---|---|---|
| 不重複訪客 | 7,800 | 6,900 | -900 (-12%) |
| 銷售營收 | $79,635.02 | $91,670.46 | +$12,035.44 (+15%) |
| 企業訂閱 | $290.70 | $290.70 | 0 |
| 權利金 | $3,777.52 | $2,969.62 | -$807.90 (-21%) |
| 總營收 | $83,703.24 | $94,930.78 | +$11,227.54 (+13%) |
| 利潤 | $26,359.62 | $28,454.42 | +$2,094.80 (+8%) |
整體而言,營收與利潤大致持平。營收較 7 月小幅成長,但我認為這主要是因為我們在 Amazon 上的商品在 7 月多數時間排名被調降所致。
我在定期訂閱制上的失敗嘗試
上個月,我試圖找出各種方法來評估更嚴格執行 TinyPilot 授權限制是否值得。我決定最划算的做法是提供授權自動續訂的選項。
Shopify 本身並未原生支援定期訂閱,因此我得在 50 多個提供此功能的第三方 Shopify 應用程式中搜尋。問題在於,幾乎所有這類應用程式都是為實體商品設計的。少數支援數位商品的,又只適用於原生的 Shopify 商店,而我的商店並非原生商店。
題外話:在 Shopify 上尋找外掛程式真的是糟透了。很少有外掛提供公開的展示,所以要了解它們的功能,唯一的方法就是實際安裝到你的商店,並授與它們存取所有商品與客戶資料的完整權限。我不願意這麼做,所以我用了一個沒有任何真實客戶資料的測試商店,但很多功能在商店沒有完整資料的情況下根本無法運作。而且,因為外掛取得了我真實的 Shopify 電子郵件地址,我現在會收到一大堆來自那些我只在測試商店安裝一小時就刪除的應用程式所寄來的垃圾郵件。
目前我的選項有:
- 完全在 Shopify 之外銷售可續訂的訂閱(例如透過 Paddle、LemonSqueezy、Stripe)。
- 將 TinyPilot 的購買流程轉換為原生 Shopify 商店,然後再回頭研究 Shopify 的第三方訂閱應用程式。
(1) 需要開發團隊建置大量基礎設施來支援 Shopify 之外的結帳流程,並確保我們的客服團隊仍能在 Shopify 之外存取客戶資訊。
(2) 則能讓所有資料都整合在 Shopify 中,但同樣也是一項大工程。上次我請人估價時,對方報價 2 萬美元。這件事我遲早想做,因為原生 Shopify 商店還有很多其他好處,但我目前沒有餘裕去處理。
讓 TinyPilot 不再那麼可設定
TinyPilot 最大的技術債來源之一是我們對 Ansible 的使用。當初創建 TinyPilot 時,我還不知道如何在 Linux 上散布軟體。我只會用 Ansible,所以 TinyPilot 的安裝程式就是一個精簡的 shell 指令碼,先啟動 Ansible,再由 Ansible 完成大部分的安裝工作。
久而久之,很明顯 Ansible 並不是適合這項工作的工具。更微妙的錯誤在於,我把安裝程式做得太可設定了。
在發布 Ansible roles 時,好的做法是把不同作業系統與硬體架構之間的差異抽象化。舉例來說,要把一組檔案複製到某個目錄,你不會直接說「把所有東西安裝到 /opt/whatever」。而是會說「把所有東西安裝到 {{ my_target_dir }}」,然後在 defaults.yml 檔案中定義 my_target_dir: /opt/whatever。這樣一來,如果 FreeBSD 系統希望安裝到不同位置,你就可以只在 FreeBSD 系統上覆寫 my_target_dir,讓它指向像 /usr/local/whatever 這樣的位置。
但 TinyPilot 只支援一種作業系統與一種硬體平台:Raspberry Pi 4 上的 Debian。
出於習慣,我把路徑、名稱與數值都抽象化,分散到不同的檔案中,但這讓我們的程式碼變得更難理解。要搞懂 Ansible 在實際安裝時會如何填入變數,往往得在三個以上的檔案之間來回對照。
誠然,確實有使用者欣賞這種彈性,讓他們能在我們官方未支援的系統上使用 TinyPilot。但這些使用者幾乎都不是付費用戶,因此我們為了支援這種彈性付出了可觀的成本,卻沒有服務到真正出資支持 TinyPilot 開發的客戶。
在最新版 TinyPilot 中,我們移除了 Ansible,同時也取消了網頁介面之外的大多數設定選項。目前沒有收到任何升級問題的回報,這強烈顯示我們的客戶其實根本不需要這種可設定性。
TinyPilot 的本質性與偶然性開發工作
在他那篇著名的文章 “No Silver Bullet,”(《沒有銀彈》) 中,Fred Brooks(佛瑞德·布魯克斯)將軟體工作分為「本質性難題」與「偶然性難題」。
本質性難題包括像是定義需求與設計使用者介面這類工作。即使你擁有完美的工具與無限的資源,如果沒搞清楚軟體要做什麼、使用者要如何與之互動,你也無法打造出有用的應用程式。
偶然性難題則包括那些只因為工具限制而必須處理的事情。舉例來說,在 C 語言中管理記憶體,就是如果我們有自動參照追蹤或無限的記憶體,就根本不需要在意的事。
最近我常常以這篇文章的角度來思考 TinyPilot 的開發工作。我們正在做的很多事,感覺都屬於偶然性難題。
我把 TinyPilot 上一個衝刺的任務分為「本質性難題」(綠色)與「偶然性難題」(紅色):

TinyPilot 2.6.1 中的任務,依本質性難題(綠色)與偶然性難題(紅色)標示顏色
有 9 項任務(24%)屬於本質性難題,例如新增或改進功能,而 28 項(76%)則屬於偶然性難題,例如回歸問題、套件更新或重構。
我沒有很好的方法能以開發時數來衡量投入程度,但我懷疑偶然性難題的任務平均花費時間比本質性難題的任務更長。我們可能有多達 90% 的時間都花在偶然性難題上。
如何減少偶然性難題?
當我進一步思考這種分類後,我發現它不太符合我對 TinyPilot 開發工作的看法。我在意的有三種類別,以及我大致希望投入在每一類上的時間比例:
| 類別 | 理想投入比例 |
|---|---|
| 改善產品 | 70% |
| 自動化與降低複雜度 | 20% |
| 例行維護 | 10% |
問題在於,這些比例很難取得平衡。每一行新增的程式碼都會增加維護成本。一個 5 萬行的程式碼庫所需的維護工作量,至少會比一個 3,000 行的程式碼庫多出一個數量級。
誠然,投入 20% 來消除複雜度應該能降低維護成本,但它不一定能抵銷新功能帶來的負擔。去年我們新增了對 H.264 視訊的支援,但為此必須整合第三方 WebRTC 伺服器 Janus。WebRTC 極其複雜,因此單是這一個功能就讓我們的維護負擔一夕之間增加了 20% 至 30%。
再深入思考後,或許這正是套用我的 50% 法則的好機會。我們應該將 50% 的時間用於改善產品,然後執行必要的維護,再把剩下的時間用於自動化與降低複雜度。
透過這個角度重新檢視上一版的更新,我們的狀況是:
| 類別 | 任務數 | 任務占比 |
|---|---|---|
| 改善產品 | 8 | 22% |
| 自動化與降低複雜度 | 26 | 70% |
| 例行維護 | 3 | 8% |

TinyPilot 2.6.1 版中的任務,依改善產品(綠色)、自動化與降低複雜度(藍色)及例行維護(紅色)標示顏色
我們偏向自動化,是因為我們大力推動移除 Ansible,但整體上比我原本想像的更接近理想的分配。
透過這三種類別的系統來看,我覺得自己在開發上的投資方向大致正確,因為在團隊規模不變的情況下,我們不可能無止盡地擴充功能。
總結
完成了什麼?
- 發布了 TinyPilot Pro 2.6.1。
- 移除了 TinyPilot 安裝流程中的 Ansible,大幅提升效能並降低複雜度。
- 發布了 “Aardvark’d:18 年後的 Fog Creek 紀錄片”。
經驗教訓
- 無法永遠不斷開發新功能。
- 隨著軟體專案日趨成熟,你要嘛得增加開發人員來處理額外的維護,要嘛得將重心更轉向簡化。
- 可設定性會帶來隱性的維護成本。
- 專案中的每一個設定選項都會讓行為更難理解,並增加修改的成本。只保留真正需要的可設定選項。
- 別以為有專案經理,專案就一定會被妥善管理。
- 我因為對方有自己的專案經理,就不再操心轉移至委外製造商的專案管理。回頭看,我應該更積極地追蹤待辦事項。
下個月的目標
這有點取巧,因為這篇回顧寫得比較晚,所以實際上是未來一週的目標。
- 盡快將製造轉移至委外製造商。
- 委派清理 TinyPilot 辦公室的工作。
- 用完所有剩餘的 Raspberry Pi 來組裝 TinyPilot 裝置。
隨機一篇部落格