2.7 popclient 变成了 fetchmail
这个项目真正的转折点是在 Harry Hochheiser 发来了他的代码草稿之后,看完这段将邮件转发到客户端机器 SMTP 端口的代码,我立刻意识到,如果能可靠地实现这个特性,其他的邮件投递模式都可以废弃了。
曾经有好几周,我都在一步步对 fetchmail 进行微调,我觉得 fetchmail 的界面设计虽然可用但有点乱,不够优雅,选项琐碎且遍布各处,尤其是那个将已收取邮件导出为邮箱文件或打印到标准输出上的选项让我感到很烦,虽然我也说不出为什么。
(如果你不关心互联网邮件的技术细节,尽可以跳过下面两段。)
当考虑 SMTP 转发的时候,我发现 popclient 想做的事太多了。它被设计为既是一个邮件发送代理(MTA)又是一个本地的邮件投递代理(MDA),有了 SMTP 转发,就应该把 MDA 去掉而成为一个纯 MTA,由它将邮件再发给其他诸如 sendmail 之类的本地投递工具。
在所有支持 TCP/IP 的平台上都已缺省保证 25 号端口打开的情况下,为什么还要折腾复杂的邮件投递代理配置或者去设置邮箱的“加锁并添加”(lock-and-append)选项?而采用 SMTP 转发带来的另一个特别好处是:取回的邮件看上去就像原发的 SMTP 邮件一样,这可正是我们想要的。
(返回到非细节层面……)
即便你没有读上面那些技术细节,下面还是给你准备了一些重要经验。我特意模仿 Linus 方法后所得到的最大收获是“SMTP 转发”概念,是用户给了我这个极妙的想法——我所做的只是去理解其意义。
11.仅次于拥有好主意的是,识别来自用户的好主意,有时后者会更好。
很有趣的是,如果你发自内心地谦逊,并承认你欠别人很多,你将很快发现世界会这样对待你:他们认为是你发明了整个软件,而且你对自己的天赋有着得体的谦虚。我们可以看到这一点在 Linus 身上体现得有多好!
(当我 1997 年 8 月在 Perl 大会上第一次说出这些的时候,黑客泰斗 Larry Wall 在我前一排,他以一种宗教复兴般的状态大声喊道:“说下去,说下去,兄弟!”所有的听众都笑了,因为他们知道这一点在 Perl 发明人 Larry Wall 身上也适用。)
我以这样的精神将项目运行几周后,开始收到同样的赞扬之词,不仅仅来自于用户,也来自一些对此有所耳闻的人。我把这些邮件收藏了起来,如果什么时候我开始怀疑人生,我就把它们拿出来看看:-)。
接下来是两个更基础的、非政治性的经验,适用于所有类型的设计。
12.通常,那些最有突破性和最有创新力的解决方案来自于你认识到你对问题的基本观念是错的。
我曾尝试解决错误的问题,那时我总是想把 popclient 做成一个 MTA 和 MDA 的结合体,并支持各种各样古怪的本地投递模式。而事实上,应该彻底重新思考 fetchmail 的设计,它应该是一个纯粹的 MTA,成为 Internet 邮件常规 SMTP 对话路径的一个部分。
当你发现自己在开发中碰壁时,当你发现自己苦思冥想也很难做出下一个补丁时,通常你不该问自己是否找到了正确答案,而是该问你是否提出了正确的问题,因为也许问题本身需要被重新定义。
于是,我重新定义了我的问题。显然,正确的做法应该是:(1)将 SMTP 转发做到通用驱动里面;(2) 将它设为默认模式;(3)最后,将所有其他投递模式都扔掉,尤其是投递到文件(deliver-to-file)和投递到标准输出(deliver-to-standard-output)的选项。
对第 3 步我犹豫了一段时间,主要是害怕影响那些依赖其他投递模式的 popclient 老用户们。理论上讲,他们可以立刻使用.forward 文件(如果使用 sendmail——译者注)或者其他非 sendmail 软件的类似功能来获取同样的效果,但在实际操作中,这个转换可能会让人有点头大。
但当我这样做了以后,好处非常明显,驱动代码中最让人厌烦的部分不见了。配置得到根本上的简化——不再需要低声下气地围绕 MDA 和用户邮箱打转了,也不再担心底层的 OS 是否支持文件加锁。
并且,信件丢失的唯一途径也不见了——如果你让程序投递到文件而磁盘满了,信件就会丢失。而使用 SMTP 转发就不会有这种问题,因为 SMTP 监听程序只在信件被正常投递或者至少被缓存后,才会返回确认消息。
性能也得到提升了(尽管你不是运行一次就能感觉到的),另一个不太明显的好处是,用户手册也变得更简洁了。
后来,为了处理一些涉及动态 SLIP 的棘手问题,我不得不恢复了对“通过用户指定的本地 MDA 投递”的支持,但这个做起来容易多了。
这件事寓意何在?在不损失效能的前提下,不要犹豫,扔掉那些过时的特性吧。Antoine de Saint-Exupéry [3] (在不写经典儿童读物的时候,他是一名飞行员和飞行器设计师)说过:
13.“设计上的完美不是没有东西可以再加,而是没有东西可以再减。”
当你的代码变得既好又简单,你就知道你做对了,在这个过程中,fetchmail 有了自己的特点,和它的前身 popclient 不再一样了。
现在是时候改名字了。和老的 popclient 相比,新的设计看上去更像是 sendmail 的搭档,两个都是 MTA,sendmail 是先“推”(push) 后投递,新的 popclient 先“拉”(pull) 后投递。所以,在开工两个月后,我把它重命名为 fetchmail。
这个故事还告诉我们一个更通用的道理,不仅是排错过程可以并行,开发和设计也可以并行(而且能达到让人惊讶的程度)。如果你采用快速迭代开发模式,开发和改进过程就可能成为排错过程的一个特例——修复软件原先在功能或概念上的“疏漏型 bug”(bug of omission)。
即便是高层次的设计,如果能有很多合作开发者在你产品的设计空间周围探索,也是很有价值的。设想下一滩雨水是怎么找到下水口的,或者说蚂蚁是怎么发现食物的。探索在本质上是分散行动,并通过一种可扩展的通信机制来协调整体行为。这很有效,就像 Harry Hochheiser 和我,一个外围的游走者可能会在你旁边发现宝藏,而你可能有点过于专注而没能发现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论