丑陋的代码和蠢事

本周,我与我们的一位工程师就 “低劣代码 ”进行了一次对话,并由此与他分享了我的一个不同寻常的灵感来源: Flamework 是在 Flickr 上创建的一个伪框架。

两种激情,两种方法

我的工作有两种激情。一个是热爱创造美丽、优雅的代码–制作注重清晰设计和可重用性的开源库和应用程序接口。另一种激情是为实际用户(甚至可能不是开发人员)构建快速、实用的解决方案。后者通常是在构建产品的环境中,产品不是代码。在这里,速度和迭代比优美的代码或可重用性更重要,因为成功与否取决于能否交付人们想要的东西。

Flamework 服务于后者,却粗暴地违反了前者。

很早以前,我就意识到,创建可重复使用的代码和直接为用户解决问题往往是背道而驰的。我的第一条线索是在帮助运行德国的 ubuntuusers 网站时得到的。该网站使用的是经过大量修改的phpBB版本,尽管乱七八糟,但只要打上适当的补丁,就能扩展到一个庞大的用户群。它很混乱,但很容易调整。抽象概念只有一层。

那时,我和一个朋友试图用自己编写的公告板软件 Pocoo 来取代它。在没有用户的情况下孤军奋战,让我走上了过度工程化的道路。虽然我们学到了很多东西,并最终创建了流行的开源库(如 Jinja、Werkzeug 和 Pygments),但 Pocoo 始终没有成为一个可靠的产品。后来,我和我的合作者们重写了 ubuntuusers,但目的并不是让它成为一个可重复使用的产品。重写成功后,它一直活到了今天。

但我花了很多年才完全意识到这里发生了什么:在构建应用程序时,可重用性并不那么重要,但在构建库或框架时,可重用性却至关重要。

Flickr 哲学

如果您对 Flamework 不熟悉,您应该看看 Cal Henderson 2008 年在 DjangoCon 上发表的演讲(我为什么讨厌 Django)。他谈到了规模以及 Django 如何没有解决这个问题。他列举了所有对他来说很重要的事情:分片、主键使用自定义序列、放弃连接和外键、支持数据库复制设置、将数据去规范化到极致。这也是我第一次了解到通过签名将所有会话数据放入 cookie 的可能性。对我来说,这是一次难忘的演讲,因为它让我看到了其中的不足。Django(我在ubuntuusers中使用的)拥有漂亮的应用程序接口,但当时几乎不能满足Cal的需求。这次演讲让我记忆犹新。

在演讲的时候,Flamework 并不存在。它更多的是 Flickr 的一个想法和工程原理。

几年后,Flamework 出现在 GitHub 上,它并不是 Flickr 代码的开源版本,而是这些想法的重新实现。你可以访问它的代码库,看看这样的代码:

function _db_update($tbl, $hash, $where, $cluster, $shard){
    $bits = array();
    foreach(array_keys($hash) as $k){
        $bits[] = "`$k`='$hash[$k]'";
    }
    return _db_write("UPDATE $tbl SET ".implode(', ',$bits)." WHERE $where", $cluster, $shard);
}

这让我本能地感到恐惧。这是 SQL 注入吗?你应该事先使用 PHP 的 addslashes 函数。但请注意,它是如何在查询函数中直接使用分片和集群的。

混乱但有效

像这样的代码往往会引起人们的直观反应,尤其是那些崇尚简洁设计的工程师。

这样的代码是如何创建的?卡尔-亨德森(Cal Henderson)将 Flickr 的原则描述为 “做最笨的事情,但一定行得通”。也许 “愚蠢 ”一词太重了,“简单 ”可能更贴切。然而,对于那些期待精心设计代码库的人来说,“简单 ”可能会显得杂乱无章。这种情况并不少见,我就见过一次又一次。我参与的第一个大型商业项目(Plurk)也是相当务实的,而且内部一团糟。我的前同事 Ben Vinegar 最近也分享了一个关于 FreshBooks 早期凌乱代码的故事,以及他是如何接受它的。在 Sentry 也是一样。我们行动迅速,但却一团糟。

回顾过去,这些都不足为奇。如果没有为真正的人解决实际问题,完美的代码并不能保证成功。在真空中追求优雅会导致被遗弃的副项目或无人使用的框架。相比之下,笨拙但功能强大的代码往往会为了快速迭代而做出适当的妥协。而这反过来又意味着,大量杂乱无章的代码会为人们喜爱的产品提供动力–这是一个更大的挑战。

罗夏克测试

多年来,我曾向多位工程师展示过 Flamework 的代码,通常都会引起强烈的反响。它似乎无视良好软件工程的所有规则,让人睁不开眼睛。

这使得 Flamework 成为工程师们的一个有趣的罗夏克测试。你是在钦佩它对一些关键问题的关注,如规模、内置可观察性和调试工具。还是因为手动构造 SQL 查询、使用全局变量、不使用类以及看起来像乱七八糟的 PHP4 代码而对它及其创建者进行评判?Flamework 是一款实用的工具,有意设计用于大规模快速迭代,还是由不熟练的开发人员制造的一团糟?

我会使用 Flamework 吗?你好,不会。但我很欣赏它背后的优先级。如果这些丑陋的选择能帮助你加快进度、吸引用户并验证产品,那么以后的重写或大规模重构都是很小的代价。

平衡问题

归根结底,你对 “低劣代码 ”的态度取决于你的主要目标:

  • 你是在开发产品,争分夺秒地满足用户需求?
  • 还是要构建一个经得起时间考验的可重用库或框架?

这两种思维方式都有道理,但它们很少能在一个代码库中和谐共存。Flamework 提醒我们,如果能解决实际问题,杂乱、简单的解决方案也可以很强大。最终,当时机成熟时,你可以对其进行清理或从头开始重建。

真正的挑战在于决定走哪条路,以及何时走。即使经验丰富,也很难知道何时从快速修复转向更稳健的基础。Flamework 背后的原则也反映在 Sentry 的开发理念中。其中最重要的一条就是 “拥抱胶带”。然而,随着 Sentry 的成熟,我们的许多胶带都经不起时间的考验,在真正的解决方案是用混凝土浇筑坚实基础的时候,胶带却被重新贴上了。

这是因为成功的项目最终都会成长起来。一开始让你快速迭代的东西最终可能会变成一个无法维护的烂摊子,需要从里到外进行重建。

我个人绝对不会建造 Flamework,我对它有点反感。同时,我也非常敬佩那些创建 Flamework 的人。他们的工作和思想塑造了我解决问题和思考产品工程的方式。

本文文字及图片出自 Ugly Code and Dumb Things

你也许感兴趣的:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注