TinyPilot:38か月目
一行まとめ
ソフトウェアへの投資は、正しい方向に進んでいるだろうか?
初めての方へ
こんにちは、Michaelです。ソフトウェア開発者であり、独立系コンピューターハードウェア企業TinyPilotの創業者でもあります。2020年に会社を立ち上げ、現在は月商6万〜8万ドル、私以外に7人の従業員を雇用しています。
毎月、このような振り返り記事を書いて、事業と仕事全体の状況を共有しています。
ハイライト
- TinyPilotのライセンスを継続課金のサブスクリプションとして販売することに失敗しました。
- TinyPilotをあまりにも設定可能な製品にしてしまったことに気づきました。
- TinyPilotの開発への投資をうまくできていないと思っていましたが、この振り返りを書いたことで、実際にはおおむね正しい方向に進めていると気づきました。
目標の評価
毎月の初めに、その月に達成したいことを宣言しています。目標に対する結果は次のとおりです。
できるだけ早く製造を委託先メーカーへ移行する
- 結果:メーカー側が作業を進められない状態はすぐに解消できましたが、さらにスピードアップできる機会を逃しました。
- 評価:B-
委託先メーカーが私からのフィードバック待ちで止まっているときは、すぐに完全な回答を返すことを優先しました。その点では、うまく対応できたと思います。
ただ、プロジェクトをもっと積極的に管理すべきだったことに気づいたのは遅すぎました。メーカーにはプロジェクトマネージャーがいるので、すべてをきちんと管理しているだろうと思っていました。しかし、納期に遅れた場合に最も大きな損失を被るのは、結局のところ私です。
箱のデザインや取扱説明書のようなものについてフィードバックを求められると、私はすぐに返信して、その後は先方から連絡が来るまで忘れていました。最終サンプルの出荷予定日になって初めて、フィードバックを渡してから箱や取扱説明書の最終稿を一度も見ていなかったことに気づきました。デザインにはさらに修正が必要で、そのせいで余計な遅れが生じました。
TinyPilotの現地オフィスから移転するための詳細な計画を作る
- 結果:月ごとの移転計画を作成し、目標日とマイルストーンを設定できました。
- 評価:A
計画ができ、スケジュールについて全員の認識もそろっています。
売却したい機材の一部については、いまだに「鶏が先か卵が先か」という問題があります。たとえばプリンターを売ったら、ほかのものを売るための配送ラベルをどうやって印刷すればいいのでしょうか。ただ、最悪の場合は、残ったものを自宅に保管して、ここから販売すれば済みます。
TinyPilotライセンスの自動更新オプションを試す
- 結果:いくつかの選択肢を評価しましたが、うまく機能するものはありませんでした。
- 評価:B
TinyPilot Proのライセンスを継続課金のサブスクリプションとして販売できるShopifyアプリを見つけたいと思っていましたが、見つけられませんでした。この件については、下で詳しく説明します。
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月と比べると収益は少し増えていますが、主な理由は、7月の大半にわたってAmazonの商品ページの検索順位が下がっていたことだと思います。
継続課金サブスクリプションへの挑戦と失敗
先月は、TinyPilotのライセンス制限をより厳格に適用する価値があるかどうかを評価する方法を考えました。費用対効果が最も高い解決策は、ライセンス購入時に自動更新オプションを提供することだと判断しました。
Shopifyには継続課金サブスクリプションのネイティブ対応がないため、この機能を追加する50以上のサードパーティ製Shopifyアプリを探す必要がありました。問題は、そのほとんどが物理商品向けに設計されていることです。デジタル商品に対応している数少ないアプリも、ネイティブなShopifyストアでしか動作しません。私はそのようなストアを持っていないのです。
余談ですが、Shopifyのアドオン探しは最悪です。公開デモを用意しているものはほとんどありません。そのため、何ができるのかを確認するには、実際に自分のストアへインストールして、すべての商品と顧客データへの完全なアクセスを許可するしかありません。私はそんなことをするつもりはないので、実際の顧客データがないダミーストアを使いました。しかし、ストアに十分なデータが入っていないと機能しないものが多くあります。しかも、そのアドオンには私の本物のShopifyメールアドレスが登録されるため、ダミーストアに1時間だけインストールして削除したアプリから、大量のスパムが届くようになってしまいました。
現時点での選択肢は次のとおりです。
- Shopifyを完全に離れ、Paddle、LemonSqueezy、Stripeなどを使って更新可能なサブスクリプションを販売する。
- TinyPilotの購入フローをネイティブなShopifyストアへ移行し、そのうえでShopifyのサードパーティ製サブスクリプションアプリを改めて検討する。
(1)では、Shopify外のチェックアウトに対応するためのインフラを大量に開発し、サポートチームがShopify外にある顧客情報にも引き続きアクセスできるようにする必要があります。
(2)ならすべてをShopifyに集約できますが、こちらも大規模なプロジェクトです。最後に見積もりを依頼したときは、移行に2万ドルかかると言われました。ネイティブなShopifyストアにはほかにも多くのメリットがあるので、いずれは実現したいと思っています。ただ、今は取り組む余力がありません。
TinyPilotの設定可能性を減らす
TinyPilotの技術的負債を生んでいる最大の要因の一つが、Ansibleの利用です。TinyPilotを作ったとき、Linux上でソフトウェアを配布する方法を知りませんでした。Ansibleの使い方は知っていたので、TinyPilotのインストーラーは、Ansibleを起動し、インストールの大部分をAnsibleに任せるだけの最小限のシェルスクリプトでした。
時間が経つにつれて、Ansibleはこの用途に適したツールではないことが明らかになりました。さらに気づきにくかった間違いは、インストーラーをあまりにも設定可能にしていたことです。
Ansibleのロールを公開する場合、OSやハードウェアアーキテクチャの違いを抽象化するのはよい慣行です。たとえば、一連のファイルをディレクトリにコピーするとき、単に「すべてを/opt/whateverにインストールする」とは書きません。「すべてを{{ my_target_dir }}にインストールする」と書き、defaults.ymlファイルでmy_target_dir: /opt/whateverを定義します。そうすれば、FreeBSDでは別の場所へのインストールが必要になったとしても、FreeBSDの場合だけmy_target_dirを/usr/local/whateverのような場所に上書きできます。
しかし、TinyPilotが対応しているOSとハードウェアプラットフォームは、Raspberry Pi 4上のDebianだけです。
私は習慣で、パスや名前、値を別々のファイルに抽象化していました。しかし、そのせいでコードの動作を理解するのがずっと難しくなりました。実際のインストールでAnsibleがどのように変数を設定するのかを理解するには、3つ以上のファイルを行き来しなければならないことがよくありました。
もちろん、この柔軟性のおかげで、公式には対応していないシステムでもTinyPilotを使えることを評価していたユーザーはいました。ただ、そのユーザーのほとんどは有料顧客ではありませんでした。そのため、TinyPilotの開発費を負担してくれる顧客には役立っていない柔軟性を支えるために、大きなコストを負担していたことになります。
最新のTinyPilotリリースではAnsibleを廃止し、Web UIの外にある設定オプションもほとんどなくしました。アップグレードに関する問題の報告は一件もなく、これは、そもそも顧客の誰もこの設定可能性を必要としていなかったことを強く示しています。
TinyPilotにおける必須の開発作業と偶発的な開発作業
Fred Brooksは有名な論文「No Silver Bullet(銀の弾丸はない)」で、ソフトウェア開発を「本質的な困難」と「偶発的な困難」に分けています。
本質的な困難には、要件の定義やUIの設計などが含まれます。ツールが完璧でリソースが無限にあったとしても、ソフトウェアが何をするのか、ユーザーがどう操作するのかを決めなければ、役に立つアプリケーションは作れません。
偶発的な困難とは、使っているツールの制約があるために、私たちがやらなければならないことです。たとえばC言語でのメモリ管理は、自動的な参照追跡や無限のRAMがあれば気にする必要のない問題です。
最近、TinyPilotの開発作業について、この論文のことをよく考えています。私たちがやっていることの多くは、偶発的な困難に感じられます。
TinyPilotの直近のスプリントで行った作業を、「本質的な困難」(緑)と「偶発的な困難」(赤)に分けてみました。

TinyPilot 2.6.1の作業を、本質的な困難(緑)と偶発的な困難(赤)に応じて色分けしたもの
9件(24%)は機能の追加や改善といった本質的な困難に関する作業で、28件(76%)はリグレッション、パッケージの更新、リファクタリングといった偶発的な困難に関する作業でした。
開発時間を使って作業量を正確に比較する方法はありませんが、平均すると、偶発的な困難への対応のほうが本質的な困難への対応より長くかかったのではないかと思います。私たちは時間の最大90%を偶発的な困難に費やしている可能性があります。
偶発的な困難をどう減らすか?
この内訳についてさらに考えていくうちに、これは私がTinyPilotの開発作業を捉えている方法とは少し違うことに気づきました。私が重視しているカテゴリーは3つあり、それぞれにおおよそ次の割合で時間を使いたいと考えています。
| カテゴリー | 理想的な労力の割合 |
|---|---|
| 製品の改善 | 70% |
| 自動化と複雑さの削減 | 20% |
| 通常のメンテナンス | 10% |
問題は、この数字のバランスを取るのが難しいことです。コードを1行追加するたびに、メンテナンスのコストが増えます。5万行のコードベースには、3,000行のコードベースと比べて、少なくとも桁違いに多くのメンテナンスが必要になります。
もちろん、複雑さをなくすための20%の投資によってメンテナンスコストは下がるはずですが、新機能による負荷を常に相殺できるとは限りません。昨年、H.264動画への対応を追加しましたが、そのためにサードパーティ製のWebRTCサーバーであるJanusを統合する必要がありました。WebRTCは非常に複雑なので、この一つの機能だけで、メンテナンスの負担が一晩で20〜30%増えました。
ここまで考えると、これは私の50%ルールを適用するよい機会なのかもしれません。まず時間の50%を製品の改善に使い、必要なメンテナンスを行い、そのうえで残った時間を自動化と複雑さの削減に使うべきです。
この考え方で直近のリリースを見直すと、次のようになります。
| カテゴリー | 作業数 | 作業の割合 |
|---|---|---|
| 製品の改善 | 8 | 22% |
| 自動化と複雑さの削減 | 26 | 70% |
| 通常のメンテナンス | 3 | 8% |

TinyPilotの2.6.1リリースの作業を、製品の改善(緑)、自動化と複雑さの削減(青)、通常のメンテナンス(赤)に応じて色分けしたもの
Ansibleの廃止に大きく取り組んだため、自動化に偏っていました。それでも、思っていたより理想の割合に近い配分でした。
この3カテゴリーの枠組みで見ると、開発への投資は正しい分野に向けられていると感じます。チームの規模を一定に保ったまま、機能を無限に増やし続けることはできないからです。
まとめ
できたこと
- TinyPilot Pro 2.6.1をリリースしました。
- TinyPilotのインストールプロセスからAnsibleを削除し、大幅な性能向上と複雑さの削減を実現しました。
- 「Aardvark'd:Fog Creekのドキュメンタリー、その18年後」を公開しました。
学んだこと
- 新しいソフトウェア機能を永遠に作り続けることはできません。
- ソフトウェアプロジェクトが成熟するにつれて、増え続けるメンテナンスに対応するために開発者を増やすか、よりシンプルにする方向へ重点を移す必要があります。
- 設定可能性は、気づきにくいメンテナンスコストを生みます。
- プロジェクトに設定オプションを一つ追加するたびに、動作の理解が難しくなり、変更のコストが増えます。本当に必要なオプションにだけ設定可能性を限定すべきです。
- プロジェクトマネージャーがプロジェクトを最適に管理しているとは限りません。
- 製造を委託先メーカーへ移行する際、先方に専任のプロジェクトマネージャーがいるので、プロジェクト管理について考えるのをやめてしまいました。振り返ってみると、未完了の作業をもっと積極的に追い続けるべきでした。
来月の目標
今月はもう終わりかけてからこの振り返りを書いているので、少しずるをしています。実質的には来週の目標です。
- できるだけ早く製造を委託先メーカーへ移行する。
- TinyPilotのオフィスを片づける作業を人に任せる。
- 残っているRaspberry Piをすべて使ってTinyPilotデバイスを組み立てる。
記事をランダムに読む