TinyPilot: Month 38

Michael Lynch

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,8006,900-900(-12%)
销售收入$79,635.02$91,670.46+$12,035.44(+15%)
企业订阅$290.70$290.700
版税收入$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 邮箱地址,我现在收到了大量垃圾邮件,都来自那些我只在测试店铺里装了一小时就删掉的应用。

目前我的选择有:

  1. 完全在 Shopify 之外销售可续订的订阅(例如使用 Paddle、LemonSqueezy、Stripe)。
  2. 把 TinyPilot 的购买流程转换为原生 Shopify 店铺,然后再重新考虑 Shopify 的第三方订阅应用。

方案 (1) 需要开发团队搭建大量基础设施,以支持 Shopify 之外的结账流程,并确保我们的客服团队仍能在 Shopify 之外访问客户信息。

方案 (2) 能让一切集中在 Shopify 里,但同样是个大工程。上次我请人估算时,对方报价 2 万美元来做这次转换。这确实是我最终想做的事,因为原生 Shopify 店铺还有很多其他好处,但我目前没有精力接手这个项目。

降低 TinyPilot 的可配置性

TinyPilot 最大的技术债来源之一是我们使用 Ansible。创建 TinyPilot 时,我不知道如何在 Linux 上分发软件,但我会用 Ansible,所以 TinyPilot 的安装程序是一个极简的 shell 脚本,它启动 Ansible,然后由 Ansible 完成安装的繁重工作。

随着时间推移,越来越明显的是,Ansible 并不适合这项工作。而更隐蔽的错误是,我把安装程序做得过于可配置了。

在发布 Ansible role 时,把操作系统和硬件架构之间的差异抽象出来是良好的实践。例如,要把一组文件复制到某个目录,你不能直接写“把所有东西安装到 /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,同时也取消了 web UI 之外的大多数配置选项。目前没有任何升级问题的报告,这强烈表明我们的客户根本不需要这种可配置性。

TinyPilot 的本质性与偶然性开发工作

在 Fred Brooks(弗雷德·布鲁克斯)那篇著名的文章《No Silver Bullet(没有银弹)》中,他将软件工作分为“本质性困难”和“偶然性困难”。

本质性困难包括定义需求、设计 UI 之类的工作。即使你拥有完美的工具和无限的资源,如果你搞不清软件要做什么、用户如何与它交互,你也无法创建出一个有用的应用。

偶然性困难则是指那些仅仅由于我们工具的局限性才不得不做的事情。例如,在 C 语言中管理内存就是这样一件事——如果我们有自动引用追踪或无限的内存,我们根本不会在意它。

最近我经常结合 TinyPilot 的开发工作思考这篇文章。我们做的很多事情感觉都属于偶然性困难。

我把 TinyPilot 上一个冲刺的任务分成了“本质性困难”(绿色)和“偶然性困难”(红色)两类:

TinyPilot 2.6.1 中的任务,按本质性困难(绿色)与偶然性困难(红色)着色

九个任务(24%)属于本质性困难,比如新增或完善功能;而 28 个任务(76%)属于偶然性困难,比如修复回归问题、软件包更新或重构。

我没有按开发工时来衡量投入的好办法,但我怀疑我们的偶然性困难任务平均耗时比本质性困难任务更长。我们可能有多达 90% 的时间花在了偶然性困难上。

如何减少偶然性困难?

进一步思考这个划分之后,我发现它并不完全符合我对 TinyPilot 开发工作的思考方式。我关心的是三个类别,以及我大致希望在每一类上投入多少时间:

类别理想投入比例
改进产品70%
自动化与降低复杂度20%
常规维护10%

问题在于,这些数字很难平衡。每一行新代码都会增加维护成本。一个 5 万行的代码库所需的维护工作量,至少是一个 3000 行代码库的一个数量级以上。

当然,投入 20% 来消除复杂度应该能降低维护成本,但它并不总能抵消新功能带来的负担。去年我们增加了对 H.264 视频的支持,但必须集成第三方 WebRTC 服务器 Janus。WebRTC 极其复杂,仅这一个功能就让我们的维护负担一夜之间增加了 20-30%。

再进一步思考,也许这正是运用我的 50% 规则的好机会。我们应该用 50% 的时间改进产品,然后进行必要的维护,再把剩余的时间用于自动化和降低复杂度。

用这个视角重新审视上个版本,我们的情况是:

类别任务数任务占比
改进产品822%
自动化与降低复杂度2670%
常规维护38%

TinyPilot 2.6.1 版本中的任务,按改进产品(绿色)、自动化与降低复杂度(蓝色)和常规维护(红色)着色

我们在自动化方面占比偏高,因为我们大力推行了移除 Ansible 的工作,但实际情况比我预想的更接近我的理想分配。

通过这个三类别体系来看,我觉得我在开发上的投入方向是对的,因为在团队规模保持不变的情况下,我们不可能无限扩展功能。

总结

完成了什么?

经验教训

  • 你不可能永远开发新功能。
    • 随着软件项目走向成熟,你要么增加开发者来应对额外的维护工作,要么把重心更多地转向简洁性。
  • 可配置性会带来隐蔽的维护成本。
    • 项目中的每一个配置选项都会让行为更难理解,并增加变更的成本。应把可配置性限制在真正需要它的选项上。
  • 不要假设项目经理会把项目管理做到最优。
    • 转移到合同制造商时,因为他们有自己的项目经理,我就不再操心项目管理了。事后来看,我本应该更积极地跟进未完成的任务。

下个月的目标

这有点作弊的成分,因为这篇回顾是在月末才写的,所以实际上是接下来一周的目标。

  • 尽快将生产制造转移到我们的合同制造商。
  • 委派清理 TinyPilot 办公室的任务。
  • 用完所有剩余的 Raspberry Pi 来组装 TinyPilot 设备。

原文由 Michael Lynch 发布

本文章由 stealth/ox-alpha 进行翻译