我的巨著在90年代晚期,我为一家小型并且扩张迅速的邮件订购零售商创建了一个网站。一开始,这个站点只包含一些静态的页面——关于商品的小册子,一个导航菜单和一个访问数量计数器。 当我们的访问量越来越大时,我们决定加入电子商务功能。我们遍历了一些现成的工具包,它们的质量从差到极差不等。我印象中第一个版本大部分建立在摆 弄cgi脚本以及怪异地把<select>元素用于几乎所有的用户交互部分之上。之后的一个版本是充斥着framesets和 Javascript的怪物——远在Javascript成为举世皆准的构建应用功能的方式之前。另一个版本是由微软的Access数据库驱动的。 不久后我们意识到,如果我们想要一个真正可用的甚至体面的在线商店,我们需要一个自定义解决方案。我想到了我过去的成功经验:fuck生成器系列, 以及截至那时我所编写的优秀网站,这其中:我的 Manic Street Preachers吉他谱收藏网站非常具有权威性。我认为是时候看看我能真正做些什么的时候了。我要自己从头开始干。 从头开始?即使那个时候开源框架已经存在,我也不会知道他们。我有自己的计划。我买了一本关于PHP和MySQL的书,一边学习一边着手搭建新的网站。 幸运的是,这本书把一个非常简单的购物网站作为它的核心例子。所有的部分都在那儿:“category.php”会列出一个目录中的所有物 品;“product.php”会显示商品信息以及把该商品加入购物车的按钮;以及最重要的“cart.php”,它是所有奇迹发生的所在。这就是我想要 的东西! 我孜孜不倦地学习这个例子,充满自信地实现所有巧妙的而且毫无疑问也是最新潮的技术-那些方便的“mysql_”函数;用于建立查询的字符串连接函 数;把不同的函数放进“functions.php”文件;通过加入“header.php”和“footer.php”来维护整个网站的一致性;为了代 码的快速运行而回避了笨重的面向对象的设计方式(管它是什么玩意)。我的技能在飞速成长。 像一个人的王国一样,我建造了高塔和迷宫般的地道。我每添加一个特性,就好像整个结构在向天空伸展同时也向地下蔓延。顾客帐户、商品评价、购买历 史、优惠点数、帐单号、特殊优惠、日志、 A/B测试、支付信息加密,等等。一个蔓延的迷宫,一整个星系的函数,大的小的,缓缓围绕一个不变的核心:“cart.php”。 经过八个月的激情工作,我终于完成了。 现在,你们这些读者一定在期待我会详述当网站正式运行时发生了怎样恐怖的事情。恐怕我要让你们失望了。 它成功运行了。
最糟的方法尽管我现在把这当作我最糟的设计,但是这个东西确确实是能够运行。它在每一个糟糕的教程,每一个反php的帖子里都能找到。搅成一团的代码?是的。不一致的数据和方法名称?是的。介绍和业务逻辑混在一起?是的。魔幻数和全局变量?是的。 对我而言,面向对象的设计只是一堆不必要的开销和公式化的代码,并且有很多片面的理论支持我的观点。我知道有关测试的所有,点击一些你设计的特性,看上去不错,上传运行。我不太知道别的架构,但是据我所知,我所采用的是最明智的方法。 一些事实能“证明”我所做的都是正确的:我从零开始,白手起家,用智慧创造了一个功能齐全的电子商务站点。更重要的,它运行完好并且还在扩张。 在我的眼里,我和那些写了亚马逊的程序员们没什么太大区别。当然亚马逊要大一些,但是我没有看到任何我的网站不能扩张成那样的理由--尤其考虑到我采用的高速运行的架构。 我认为我的技术水平已经到了巅峰了。并不是说我对学习新技术不感兴趣了,我只是不再对此感到紧迫。毕竟我创造了一些不错的产品。任何在此之上的东西只是附加奖励,是蛋糕顶端的樱桃而已。
回到地表我很遗憾,我在这种心态下生活了好几年。我只是将一小部分时间用在这个网站上,而把主要时间用在完全不同的领域。在之后多年的维护和偶尔添加特性的 过程中,我确实意识到了之前做的一些选择是有问题的。我意识到有时候要花很长时间才能找到我要找的文件。有时候当我做一个改动时,一些看上去毫无关联的地 方会出现bug。 我的学习没有停止,但它确实进展缓慢。我意识到我曾经写的mysql函数是有风险的,因为后面版本的PHP减少了对它们的支持。在一段时间里,我克 服对此的恐惧的方法是坚信我的无懈可击的设计可以弥补这些风险。毕竟我尝试了所有形式的我能找到的SQL注入,一切看起来都没有问题。 去年的一天我接到了一个紧急电话,网站挂了。所有的请求都得到500错误。在工程师们重新启动并且分析了事故原因后,这被证实是一起来自国外的sql注入攻击,是我从来没见过的一种。 好吧,我想,这也许是我该转向PDO的时候了。
觉悟当我坐下来准备重写所有的数据存取方法时,我意识到了一些深层次的问题。我意识到这将会很困难。而且我知道为什么它会这么困难。 因为这些方法散落在所有地方;因为我无法知道是否会不经意地破坏一些东西;因为代码是如此不一致以至于我要小心地研究不同对象的细微差别;因为很多 代码和别的部分紧密相连,这也会导致我会不小心造成破坏。简单地说,这将会很困难。不仅因为所有这些坏的实现方法,还因为我对它们所将造成的后果缺乏预 见。 所有的辩护,借口,逃避都无法继续下去了。我错了。我不是那个幻想中的天赋卓越的程序员。这么多年来,我一直都没有认清这一点。 我的愚蠢已经显而易见,尽管这对我的自尊心是极大的打击,但这也是很宝贵的一个教训。我通过亲身经历(而且是非常痛苦的),学到了为什么做一件事的 方法有对错之分。这不仅仅关系到品味或者时尚。这不是比谁的方法更聪明。正确的方法可以在现实生活中找到,并且能让你和那些使用你代码的人的生活更好。错 误的方法让人沮丧,浪费时间。我在这里不想说明哪些东西是组成“正确方法”的要素。只要说不是我做的那些就够了。
真正的错误我实现了PDO。同时开始第一次使用PHPUnit。我决不想尝试通过单元测试去改造那样的代码。 现在我有意识地迫使自己无论何时都尽量去学习。我正在读一些每个程序员都应该读的书。我在关注别人的博客。我在收听播客。我会看会议视频。我正在参加一些当地的社团并且在其中做演讲。我在做副业并且挑战自己学习新的技术。我在学习用正确的方法做事。 对你们所有献身于这项事业中的人来说,有一个对我们很重要的有利条件。即编程是这样一个完全抽象的活动,任何其他领域都会受到的现实世界中的限制在这里不存在。在这里,你的极限是你自己。 我要以一些真正的箴言结束这个故事。我在开始写这片博客的时候正好刚看完第二版的《代码大全》。在书的最后,第825页的底部,作者准确地描绘了我在写这篇文章时的想法。可以说他只用了两句话就表达我在这数千字里想表达的东西: “作为一个初学者或者进阶者,这并没有什么错。当一个有能力的程序员而不是领导者,这也没有什么错。真正的错误是,当你知道应该如何去提高时仍然选择做一名初学者。” 译文链接: http://blog.jobbole.com/72179/ |