MMatt Senter
现在项目关于联系博客

← Blog

构建防御性软件

2026年9月23日 · Matt Senter

Mac 上 Comoji 的菜单栏菜单,托盘图标上带有一个锁形徽标,横幅显示 Secure Input Is On, Held by Google Chrome。

简短版本

防御性软件指的是当周围环境出现异常时仍然让人能够理解的软件,而不只是校验输入的软件。Comoji 是我做的 Mac 表情符号自动补全应用,在一次重启之后它看起来坏掉了,原因是 macOS 的 Secure Input 卡在了开启状态,先是被 Chrome 持有,之后又被 Apple 的登录进程持有。键盘输入受保护时 Comoji 主动退让,这个行为是正确的,但没有任何东西把这一点告诉用户,于是一次有意的暂停看起来和崩溃一模一样。根本原因在我的代码之外,但这份困惑依然归我负责。修复很小,也没有削弱保护:只要 Secure Input 处于活动状态,菜单栏图标上就显示一个锁形徽标,并且一键就能打开一篇帮助文章,解释这个状态意味着什么以及如何清除它。当产品看起来坏了的时候,技术上正确并不能带来安慰。有时候正确的行为就是停下来,而好的软件应该说明原因。

前不久,我的笔记本电脑电量耗尽关机了。插上电源重启之后,Comoji 不工作了。我输入一个表情符号快捷键,什么也没有发生。没有解释,没有任何迹象表明有什么变了。只有一个关机前还好好的、现在显然不再工作的应用。

Comoji 是我做的一个小型 Mac 应用,在你已经在用的文本框里提供表情符号自动补全。它的工作很简单:识别一个快捷键,给出匹配的表情符号,然后别碍事。当这一切停止发生时,这个产品就没剩下多少可以交互的东西了。

只不过 Comoji 其实并没有坏。它是在尊重一条安全边界,而我电脑上的另一个东西卡在了这条边界后面。

为什么做正确的事看起来会像什么都没做?

macOS 有一项叫作 Secure Input(安全输入)的功能,用来防止敏感的键盘输入被其他进程截获。这对 Comoji 这样的应用很重要,因为它需要识别你输入的内容才能提供自动补全。一个表情符号工具没有任何理由在你输入密码时旁观,所以在 Secure Input 处于活动状态时,Comoji 会主动退让。

这一次,Chrome 显示为持有 Secure Input 的进程,尽管我早已不在输入密码了。我强制退出了 Chrome,以为这样就能解决问题。结果 Secure Input 仍然处于活动状态,而且现在关联到了 Apple 的登录进程上。不管重启之后哪里出了岔子,关掉浏览器都不足以解决它。

这是 Apple 自己也记录在案的一类问题。一个应用在不再需要 Secure Input 时仍让它保持开启,可能会干扰其他依赖键盘事件的应用,即使肇事的应用已经退到了后台。安全机制继续尽职尽责,机器上其他地方的软件却要承担后果。

Comoji 当时就处在这样的境地。系统说键盘输入需要保护,它就正确地拒绝工作。但这一切对我来说都是不可见的。这个应用是我写的,可就连我一开始也只看到它不工作了。

用户不应该需要去调查操作系统的状态,才能弄明白为什么自己的表情符号快捷键突然什么都不做了。

为什么「不是我的 bug」不算一个完整的回答?

一旦确定问题源自你的代码之外,就停止追查,这种诱惑可以理解。Chrome 持有 Secure Input。然后 Apple 的登录进程也牵涉其中。Comoji 的行为完全符合设计。结案了,对吧?

并不是。这解释了是谁造成了问题,却帮不到那个正试图使用我的应用的人。他们不会去看进程归属,也不会去判断哪家公司该收到 bug 报告。他们看着 Comoji,纳闷它为什么不工作。

底层的输入问题在 Comoji 之外。而缺少解释这件事,是我可以修的。

防御性软件开发正是在这里超越了检查非法参数和捕获异常。你还得考虑,当周围的环境不再正常运转时,你的应用看起来是什么样子。用户能区分有意的暂停和崩溃吗?他们知道是什么在阻止应用工作吗?你有没有给他们一个有用的下一步去处?

当你的产品看起来坏了的时候,技术上正确并不能带来多少安慰。

我改了什么?用一把小锁取代一个谜团

我给 Comoji 的菜单栏图标加了一个小小的锁形指示,只要 Secure Input 处于活动状态就会显示。现在,自动补全缺席有了一个看得见的原因:Comoji 已暂停,因为键盘输入受到保护。这个应用不再默不作声地把一个不可用的功能摆在那里,好像它本该在工作一样。

我还把托盘菜单链接到了一篇解释什么是 Secure Input 的帮助文章,以及 Comoji 为什么要尊重它。卡住的人可以直接从应用里找到这个解释,而不必猜测正确的搜索词,或者联系我才发现原来系统里有这么一项功能。

重要的是我没有试图解决什么。答案不是为了让表情符号自动补全能用而绕过密码保护。Secure Input 处于活动状态本身并不是错误。它的存在有充分的理由,Apple 也明确警告过,开启安全键盘输入可能会影响其他应用,也就是那些需要这些按键的应用。

所以这把锁传达的是一种状态,而不是一场危机。在正常输入密码期间,它解释了为什么 Comoji 暂时不可用。当这种状态意外地持续下去时,它给了用户一个理解发生了什么的起点。

我没有让 Chrome 或 macOS 变得不会卡住。我只是让它们卡住时,对使用 Comoji 的人来说不那么令人困惑,同时没有削弱 Comoji 本应尊重的那层保护。

为什么大公司依然是外部依赖?

为了 Google 的浏览器和 Apple 的操作系统牵扯出的问题,去加固一个微不足道的小工具,这件事有点让人恼火。它们不是我不假思索拖进项目里的冷门组件。它们是两家巨型科技公司的产品,而它们的行为依然成功地让我的小应用显得有缺陷。

但它们的体量并不改变工程上的决定。我不能在「凡是比我大的公司做出来的东西都永远表现正确」这个假设之上构建 Comoji。我也不能对一个沮丧的用户说,这个问题属于一家足够气派的公司,然后就认为我的工作完成了。

我不知道这种特定的事件序列发生得有多频繁。它也许很少见。但我遇到了,而结果暴露出一个有用的区分:Comoji 已经安全地处理了这个安全条件;它还没有处理好由此产生的困惑。

这并不意味着每一个假想的故障都值得一个新设置、一个警告对话框或一个恢复子系统。在这个案例里,应对措施很小,而且直接针对问题本身。一个锁形图标让这个状态可见。一篇帮助文章让它可以被理解。两者都不需要假装我能从自己的应用内部修好另一个应用。

对我来说,这是构建防御性软件的一个重要部分。你的责任不止于确保周围一切正常时你的代码能正常工作。你还需要决定,当它无法完成自己的工作时会发生什么,包括原因出在别人的错误上的时候。

有时候,正确的行为就是停下来。好的软件应该能告诉你为什么。

继续阅读

  • HumanSlop:一家市值 1101 亿美元的公司怎么会做出这么糟糕的软件?人们总在批评 AI 垃圾,但一家市值 1101 亿美元的公司上线了一个密码限 13 位、不接受运通卡 CVV 的账单网站。
  • 电子邮件很糟糕。AI 可以修复它,但前提是我们保护好收件箱。电子邮件迫切需要 AI 分类,但收件箱过于敏感,不能随意交给另一个云服务。
  • 我唯一不会用 AI 取代人的地方:客户关怀AI 可以自动化客户服务,但不应阻挡沮丧的客户与真人沟通。

这篇文章讲的是 Comoji。 阅读 Comoji 的案例研究 →

Matt Senter

Matt Senter

Founder, entrepreneur, and CEO based in Durham, NC, with 30 years building software and 14 companies and products founded. Currently Founder & CEO of Senternet and Co-Founder, COO, and CTO of BeeReady, and the builder behind Orgabot, Highwire, StockCar, Premail, Comoji, and Burly. More about Matt · Get in touch.

© 2026 Matt Senter · 由 Senternet 的 Matt Senter 创建 Senternet使用 Orgabot 构建北卡罗来纳州达勒姆为创造者而建关于博客工具隐私条款