最近在 Ruby China 看到 @huacnlee 分享的 The Rails Doctrine - 中文翻译,拜读之下受益匪浅。
唯一遗憾是原译文不大符合我的阅读习惯,因此就有了这篇译文的诞生。希望与我有相似阅读习惯的小伙伴们会喜欢。:smile:
本翻译参考了 DHH 的原文 The Rails Doctrine 和 @huacnlee 的译文,在这里对两位表示由衷的感谢!
译文中如有不当之处,欢迎指正。
Rails 信条
Ruby on Rails 能够现象般地崛起并取得如此卓越的成就,很大程度上应归功于其对新技术的运用及切入的时机。但技术优势一般会随着时间推移而逐渐削弱,而好的时机也并不总会长久相伴。Rails 为何能始终保持与时俱进,并不断扩大其影响力和社区呢?这里有必要给大众一个合理的解释。我认为最主要的原因就是其一直坚守的那些饱受争议的信条。
这些信条在过去的十年中也在不断地演进,但最重要的依旧还是那些最基础的信条。我不会自诩是这些信条的原创者。毕竟,Rails 取得的最大的成就就是:围绕诸多离经叛道的思想(主要是关于程序设计和程序员本质),融合并培养了一个如此强大的社群。
闲话到此,以下就是 Rails 中最重要的9个信条,请用心领悟:
- 尽力讨程序员欢心
- 约定优于配置
- 主厨推荐
- 多元化
- 推崇优美的代码
- 提供开发利器
- 面向综合应用
- 演进优于稳定
- 兼收并蓄
尽力讨程序员欢心
没有 Ruby 就不会有 Rails,因此,第一信条必然是来自于创造 Ruby 的核心理念。
Ruby 最初的理念确实将“程序员的开心”放在最首要的位置,也就是将它放在许多曾经驱动程序设计语言和生态圈前进的真理之前。
当 Python 推崇“完成一件事情,有且最好只有一种方式”,Ruby 却沉醉于表达方式的丰富多彩和优雅精妙。当Java因其保护程序员自身的特性而备受推崇,Ruby 却在欢迎工具里就附上了自尽的绳子。当 Smalltalk 专注于消息传递的纯粹性,Ruby 却近乎贪婪地增加关键字和构造器。
Ruby 如此与众不同是因为它非常尊重事物的多样性。而这些多样性中的绝大部分恰是为了满足程序员开心而服务的。这种追求不仅引起了 Ruby 与其他编程语言环境的争论,也开启了主流文化对“究竟什么是程序员,以及他们应该如何工作的”的认知。
Ruby 不仅了解程序员的编程感受,而且还会尽量满足甚至改善他们的编程感受。无论这些感受是不当的、异想天开的或是令人愉悦的。Matz 跨越了复杂度如此惊人的实现门槛,才让机器最终面带微笑以取悦它的人类伙伴。Ruby 充满了视觉假象,那些表面上看上去如此简洁、清晰和优美的代码,其背后的实现却如杂技般错综复杂。当然这些选择并不是没有代价的(不信的话,可以问问 JRuby 那些尝试对 Ruby 做逆向工程的人),这也恰恰是这些选择如此值得称赞的原因。
正是这种从另一个角度致敬程序设计和程序员的方式,使我深深的爱上了 Ruby。这不仅仅因为它的简单易用和充满美学的设计,也不是任何一项单一的技术成就,而是一种愿景,一种反文化。Ruby 就是程序设计领域中与现有专业程序设计模式相左,而又符合人类思维习惯的缺失部分。
我曾经说过,发现 Ruby 就像是找到了完全适合我大脑思维习惯的魔术手套。比我曾经梦想过的任何手套都要来得合用。它甚至成为了我从“写程序只是因为我需要程序”到“写程序是因为我爱上了这种思维的运用和表达方式”的转折点。就像是找到了“流动之泉”(流动指的是著作 Flow:The Psychology of Optimal Experince 中描述的一种意识状态,处在这种状态中的人通常非常愉悦,富有创造力,并且完全沉醉其中),并能随意进入其中。熟悉 Csikszentmihalyi(上述著作的作者)著作的人都应该知道,这种影响力简直是有过之而无不及。
毫不夸张的说,Ruby 改变了我,并为我设定了人生努力的方向。这种启示是如此深刻,以致于让我对布道这个 Matz 的作品充满了使命感,也就是去传播这个意义深远的作品和它的优点。
读到这里,我可以想象你们中绝大部份的人都会难以置信地摇摇头。我不怪你们。当我对程序设计的认识还处在“程序设计只不过是个工具”的阶段时,如果有人跟我描述上述经历,我也会摇头的。并且,我还可能嘲笑这种过分夸张近似于宗教语言般的描述。但这确实是我的真实想法,也是我的肺腑之言,即便这也许会让某些人或绝大部分人感到不适。
无论如何,这对Rails来说究竟意味着什么,以及这个理念是如何指引 Rails 持续演进的呢?要回答这个问题,我想先来看看另一条早期经常被用来描述Ruby的原则是非常具有启发性的:最小惊奇原则。即 Ruby 应该如你预期般运行。通过以下与 Python 对比的例子可以非常容易地理解这个原则:
$ irb
irb(main):001:0> exit
$ irb
irb(main):001:0> quit
$ python
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
Ruby 可以同时接受 exit
和 quit
来退出终端交互界面,以此来满足程序员那显而易见的需求。而 Python 则会迂腐的指导程序员如何正确完成操作,即便它已经明确地知道程序员想要干什么(因为它给出了错误信息嘛)。这就是一个非常清晰而又短小的解释最小惊奇原则的例子。
最小惊奇原则最终在 Ruby 社区失宠的原因是:这条原则本身是非常主观的。最小惊奇原则,惊奇谁呢?显然是 Matz,以及那些和 Matz 具有相似思维方式的人。但是,随着Ruby社区的逐渐壮大,和Matz思维方式相左的人数比例也越来越多,这也成为了邮件列表里那些毫无意义的争论之源。因此,这条原则最终淡出了人们的视线,以避免“甲男是否对乙物是否惊奇”的争论无处不在。
但这跟 Rails 又有什么关系呢?其实,Rails 最初就是基于一个与最小惊奇(Matz)原则相似的原则设计的。这个原则就是DHH的 璀璨微笑原则 ,简单来说就是:接口设计的最大考量是如何让我尽可能地开怀大笑。当我把这条原则写出来的时候,连我自己都觉得这听起来有些滑稽和自恋。
然而,正是由于这种最初的深度自恋才造就了 Ruby 或 Rails 这样的作品,并且这两个项目都是从单个作者的思想中迸发出来的。当然,这样说有将我自己的创作动机强加到 Matz 身上之嫌,因此我将声明缩小到我所知道的范围:我创作 Rails 纯粹是为了我自己。第一条也是最首要的原则就是为了能让我微笑。尽管 Rails 有各种各样的功能,但这些功能的最终目的都是为了能让我更好的享受人生,是为了帮助我改善为网络信息系统需求所争论不休的日常生活。
就像 Matz 一样,有时我也会做出一些愚蠢的决定来实现我自己的理念。其中的一个例子就是 Inflector,一个恰可以完成类到表映射的类(包含规则和不规则的情况),譬如 Person 类对应到 People 表、Analysis 对应到 Analyses,Comment 对应到 Comments。这种行为现在已成了 Rails 中毋庸置疑的一部分,但当我们还在宣扬该原则及其重要性的早期,争议的怒火曾经肆意蔓延。
另外一个例子是“为了减少了些许实现的工作量,却几乎触发了大量程序员的恐慌”,如:Array#second
到 #fifth
(以及额外增加 #forty_two
)。这些访问器别名曾严重冒犯了一位非常直言不讳的支持者,他认为这些过度设计(以及额外的那个 #forty_two
都快接近文明的终点了。这是一个关于42的梗,请自行搜索答案,:smile: )完全可以改写成 Array#[1]
、Array#[2]
(和 Array[41]
)。
但是,时至今日上述两个决定依然能令我开怀。我非常享受可以在测试用例或终端里输入 people.third
。这并不符合逻辑,也不高效,甚至有些病态。
但却能使我持续开怀,并由此充满信念并丰富我的人生,继而证明在服务了 Rails 12年之后依然参与其中是完全正确的选择。
和性能优化不一样,开心优化很难衡量。这使得开心优化几乎成了不科学的无谓之举。即使有些人并未完全放弃,但也觉得这是无关紧要的事情。因为程序员一直被教导要执着并攻克于可被衡量的事物,也就是那些可以明确指出A要比B好的事物。
尽管对开心的追求很难在微观角度加以衡量,但从宏观角度来看却很清晰。Ruby on Rails 社区中的很多人明显是因为这样追求的才聚集于此的。他们因拥有更好的,更能带来满足感的职业生涯而骄傲。而这条原则正好处于这些情感的汇集之地,可想而知它的成功是必然的。
因此,我们可以得出以下结论:尽可能让程序员开心可能是造就 Ruby on Rails 最关键的因素,它应该陪伴 Rails 一直走下去。
约定优于配置
一条 Rails 早期广为流传的箴言是这样的:你并不是唯一美丽的雪花。如果能放弃那些毫无意义的个人喜好,你就可以跳出诸多无谓选择的牢笼,在那些真正重要的领域快速前进。
有谁会在乎你的数据库主键采用什么格式吗?选择 id,postId,posts_id 或 pid 真的那么重要吗?这真的值得反复讨论才做出决定吗?当然不。
Rails 的部分使命就是,帮助那些创建网络信息系统的开发者在日益庞大并一再出现的决策丛林中劈荆斩棘。其实这些成千上万的的决策只需要做一次就够了,如果有人能帮你做,那就再好不过了。
约定优于配置,不仅可以让我们避免许多无谓的思考,而且为更深层的抽象提供了肥沃的土壤。如果我们可以遵循 Person 类到 people 表的映射约定,那么我们也能用相同的约定来为 has_many :people 定义的关联找到 Person 类。优良约定的威力就在于:每个广泛使用它们的领域都会受益颇丰。
不仅专家可以借此提升生产力,新手们的入门门槛也可以大大降低。Rails 中包含了如此之多的约定,以至于新手们即使都没有察觉到它们的存在就可以从中获益。就算不了解每件事情的底细,也可能创建出伟大的应用。
如果你的框架仅仅是一本厚厚的教科书,而你的新应用只不过是一张白纸,你是不可能成功的。仅仅是找出从哪里以及如何开始就会花费你大量的精力。估计项目开始的大半时间基本上就耗在寻找哪个才是正确的入口上了。
即使你已经了解了所有组件是如何一起工作的,这种情况也不会有所好转。但是,如果每次变动都有一个明确清晰的应对话,我们就可以快速地略过应用中的大部分工作。因为这些内容和其它应用中曾出现过的内容是相同或类似。所谓各得其所,物尽其用就是这个样子。从这种意义上来讲,约束甚至让那些最有能力的人获得了解放。
当然和其它事物一样,约定的力量也并不是没有风险的。Rails 如此简洁就可以完成如此多的事情,这很容易就让人觉得应用中的每个部分都可以由预定的模版来完成。但是大部分值得创建的应用总会包含一些本身特有的元素,尽管它们可能只占 %5 或 1%,但总会有的。
因此,最难的部分是知道何时我们应该打破约定。那么什么时候值得偏离正轨呢?我认为,大多数想成为唯一美丽雪花的冲动都是不明智的,并且大大低估了脱离 Rails 的成本。但是仅打破该打破的那一小部分应该是没有问题的,当然你的仔细斟酌每个细节。
主厨推荐
当你不知道餐厅里的哪些菜好吃时,如何点餐呢?如果可以让主厨帮你点的话,那么即使之前你并不知道哪些菜好吃,也可以吃上一顿美味大餐。这就是主厨推荐。一种无需成为美食专家或通过乱点一气来碰运气就可以享受一顿美餐的方法。
对于程序设计来说,这条实践带来的好处就是让别人帮你搭建技术栈。这与我们从约定优于配置得出的观点类似,只是层次更高。约定优于配置着重于为何我们应该使用单一的的框架,而主厨推荐考量的是多个框架如何协同工作。
这和保守的程序设计传统大相庭径。传统做法是提供可用的工具让程序员自行挑选,并赋予程序员自行决定的权利(其实这是一种负担)。
你肯定听说过并赞同这句话:工欲善其事,必先利其器。听起来就像是一种毋庸置疑的常识,但前提是你的有自信分辨出哪种工具才是最好的。其实,这远比想象中的要难得多。
这和之前在餐厅吃饭时遇到的问题类似,选择每个单一的库或框架并不是独立的工作,就像为一个包含八道菜的套餐挑选每道菜一样。两者的目标都需要基于整个晚宴或系统统筹考虑。
因此,在 Rails 中我们决定舍弃小利:程序员在工具箱里挑选每件工具的权利,来换取更大的利益:一整套更好的工具箱。这个决定回报颇丰:
人多势众:当大家都用默认的方式使用 Rails 时,我们就拥有了共同的体验。教授和帮助他人就会变得容易的多,同时讨论也有了共同的基础。就好比我们都在昨晚7点看了相同的节目,第二天我们的讨论就有了共同话题。这种共同的体验进而促成了更有凝聚力的社区。
日益完善的基础工具箱:Rails 作为一个全栈框架包含了许多活动组件,它们之间如何协同工作与它们单独运行具有相同的重要性。软件工程的痛点大部分不是来自于组件内部,而是组件之前的相互协作。当我们都在一起努力工作修复这些大家都会碰到的痛点(因相同的的配置和使用方式而获得一致的错误)时,这些痛点就会越来越少。
按需替换:尽管 Rails 是一个主厨推荐的技术栈,你仍然有机会替换掉其中的某些框架或类库。只是不建议你这么做。当你需要为某些特定的场景开发出一套清晰的个性化工具箱时,再来考虑这些决定吧。
即使那些最博学多才且经验丰富的 Rails 程序员也不可能抵触菜单上的每道菜(如果是的话,那他们就不会选择继续使用 Rails 了)。因此,他们会谨慎的挑选替代者,并和其他人一齐享受剩下的部分。
多元化
大家都对挑选并遵循单个核心理念来构建自己的基础架构具有强烈的诉求。这个诉求如此纯粹,以至于程序员都会自然而然地被它所吸引。
Rails 不遵循上述理念。它不是单件完美剪裁的衣服,它是一床棉被,是诸多不同理念和模式的组合体。如果将它们分开来一一对比的话,其中许多通常看来还是相互抵触的。当然,我们也不会这么去做。这又不是仅有一个赢家的超级理念锦标赛。
来看看 Rails MVC 模式中用来创建 View 的模板。默认情况下,所有从模板里抽取出来的 Helper 不过是一堆方法,甚至拥有同一个命名空间。是不是感到有点震惊和恐惧,这不就是 PHP 嘛!
但我认为,PHP 处理这些方法的方式是正确的,因为它们之间很少相互调用,就像那些从 View 模板中抽取出来的方法一样。以此为目的,使用单个命名空间来包含这些方法不仅是理性的选择,而且是一个很棒的选择。
我们偶尔也会想用更加面向对象的方式来创建 View。MVP 模式中的 Presenter 就是这样一剂解决方法之间相互依赖的良药。Presenter 中封装了一系列相互独立的方法和这些方法要处理的数据。但事实证明这种情况并不常见。
相较而言,我们将 MVC 中的 Model 看做面向对象思想的精髓所在。领域建模的乐趣就在于为对象挑选合适的名称、提高一致性和降低耦合程度。这是和 View 层完全不同的场景,因此需要另辟蹊径。
即便如此,我们也不会遵循单一理念的信条。Rails concern (定制过的 Ruby mixin) 经常用来扩展 Model。它可以和 Active Record 模式完美结合,让那些混入的方法可以直接存取它们要处理的数据。
就算是 Active Record 模式最基本的理念也会冒犯某些纯粹主义者。因为,我们将与数据库相关的操作和业务逻辑直接混合在了一起。毫无边界的融合!没错,因为这是已经证明的切实可行的方法,可用于构建需要经常访问数据库,以存取领域模型状态的网络应用。
Rails 拥有如此理想的灵活性,使得它可以处理各式各样的问题。而大多数单一模式仅在问题的某一领域运转良好,当超出其范围时就显得力所不及了。但是,通过将多个模式叠加应用,我们就可以做到全面地覆盖。最终框架的健壮性和能力将远超任何单一模式所能达到的高度。
目前,理论上来说维持众多编程模式多元共存关系的代价是高昂的。想要用好 Rails,仅了解面向对象编程是不够的,最好还能有面向过程编程和函数式编程的经验。
上述原则也同样适用于 Rails 中的其他子语言。 我们不会为你提供过多保护,以便让你不必学习那些必须掌握的知识。比如,在 View 中使用 JavaScript 或偶尔用 SQL 来构建复杂查询。至少这类保护不会以尽可能完善为目标。
降低学习曲线的方式就是让大家容易上手,在了解框架的每个细节之前,就可以做出一些有真正价值的东西。这也是为什么我们会有一个快速搭建 Hello World 方法的原因。万事俱备就等你来尝试了。
Rails 的思路是,通过让从业者尽早地创建真正有价值的东西,来鼓励他们快速成长。让他们觉得学习 Rails 是一种愉悦的过程,而不是一种障碍。
推崇优美的代码
写代码并不仅仅是为了让计算机或其他程序员易于理解,而且还要享受那种沐浴在优美代码中的愉悦感。从美学角度来说,令人愉悦的代码本身就很有价值,应该值得我们花精力去追求。这并不意味着优美的代码应该胜过其他考量,但至少应该在优先考量中占有一席之地。
那么什么才算优美的代码呢?在 Ruby 中,通常指的是 Ruby 固有语法和自定义 DSL 力量的交集。这是一条模糊的定义,但非常值得一试。
下面是一个取自 Active Record 的简单例子:
class Project < ApplicationRecord
belongs_to :account
has_many :participants, class_name: 'Person'
validates_presence_of :name
end
它看起来和DSL非常相似,其实不过是一个类定义,加上三个带 symbol 参数和选项的类方法调用。这里并没有什么魔幻的东西,不过确实优美而又简洁。简单几行声明就带来了如此强大的洪荒之力。
这种优美部分应该归功于之前提到过的那些原则,如约定优于配置。 当调用 belongs_to :account
时,我们假定 projects 表中存在一个名字为 account_id 的外键。当必须为 partcipants 关联指定 class_name 为 Person 时,我们只需定义 Person 类即可,并可以由此推断出外键和其它相关设定。
下面是另一个取自数据库迁移系统的例子:
class CreateAccounts < ActiveRecord::Migration
def change
create_table :accounts do |t|
t.integer :queenbee_id
t.timestamps
end
end
end
这个例子体现了框架威力的精髓。这里程序员遵循某种约定定义了一个类,这个类继承自 ActiveRecord::Migration
并实现了一个 change 方法。剩下的事情就可以交给框架去处理了,并且框架知道该调用这个change方法来处理。
这样程序员只需要写很少的代码就可以很好地完成工作了。上述代码不仅支持通过调用 rails db:migrate
为数据库添加一张新表,同时也支持通过调用另外一个命令从数据库中删除这张表。这和工程师自己实现这些功能,并将它们整合为工作流的方式完全不同。
优美的代码有时看起来也会显得晦涩难懂。但优美的代码不应该是为了尽可能地短小精干,而应该更加关注阅读的流畅性。
以下两条语句是等价的:
if people.include? person
...
if person.in? people
但它们在语境和侧重点上存在细微的差异。第一条语句侧重点在集合,因为它是主语。第二条语句中,主语明显是 person。这两条语句在长度上并没有太大的差别,但是我认为第二条要优美的多,特别是当它用在判断条件是关于 person 的时候,这会让我感到由衷的开心。
提供开发利器
Ruby 本身就包含了许多开发利器。不是碰巧,而是设计如此。其中最著名的应属 Monkey Patching: 一种可以修改现有类和方法的能力。
这个能力经常被嘲讽为:太简单了,即便是最普通的程序员也可以轻松掌握。以至于那些来自限制性较为严格语言阵营的人曾经常幻想:太过信任程序员可以驾驭这项能力最终会毁了 Ruby 这门语言。
因为,如果什么都可以修改的话,那还有什么可以阻止你重写 String 的 capitalize 方法,让 “something bold”.capitalize
返回 “Something Bold”
,而不是 “Something bold”
呢?这在你自己本地的应用中可能没有什么问题,但所有那些依赖于原始实现的辅助代码可能就要遭殃了。
那么有什么解决方案吗?答案是:没有。在 Ruby 中,只要有好的理由没有什么可以阻止你用利器来扫除障碍。我们会通过约定、劝说和教育来推行好的理念,而不是通过禁止使用厨房中的利器,并坚持让每个人使用勺子来切西红柿。
由于 Monkey Patching 的负面效应创造了诸如 2.days.ago
(返回两天前的日期)般的壮举。你可能会觉得这是一桩亏本的买卖。也就是说宁可没有 2.days.ago
,你也不想修改语言的原始实现。如果这就是你的观点,那么也许 Ruby 并不适合你。
也许你从那些出于安全考虑而放弃自由的人口中听说过:正是可以修改内核类和方法的能力毁了 Ruby 这门语言。然而事实刚好相反,Ruby 的蓬勃发展正是由于它为程序员提供了全新的不同看法:完全可以相信程序员可以驾驭利器。
当然仅相信是不够的,还应该教授他们使用这些利器的方法。如此一来还有利于提升整个行业水平,当然前提是大多数程序员都想成为更好的程序员,并有能力在使用利器的同时避免割伤自己的手指。这真是一个梦寐以求的想法,也是一个有悖于许多程序员对其它程序员初衷(不相信其它程序员)的想法。
当讨论开发利器的重要性时,对象总是其它程序员。我从未听过有程序员说:”我不确信自己可以驾驭这种能力,请让我远离它!“ 经常听到的却是 “我觉得其他程序员可能在滥用利器”。但这种专制的想法从未出现在我的脑海中。
正是这些开发利器将我们吸引到了 Rails 周围。Rails 提供的利器尽管没有 Ruby 提供的那么锋利,但其中有些还是颇具威力的。我们不会因为在工具箱里放置了这些利器而感到抱歉。事实上,我们对工程师们敢于尝试的渴望持有足够的信念,并为之自豪。
Rails 的许多功能一直因 “太过随意” 而饱受争议。当下最流行的一个例子是:Concern。其实它就是在 Ruby 本身内建的 Module 功能上添加了一层薄薄的语法糖,允许使用单个类来封装多个相关又可以独立理解的业务(也就是 Concern 这个名字的由来)。
Concern 功能备受指责的原因是,通过它程序员可以很容易将一些不相关的东西塞到对象中。说的没错,Concern 确实可以这样使用。
一种谬论认为,如果不提供类似于 Concern 的功能(即使是最温和的用法也会导致设计思想的部分分离),就可以让程序员处在通往幸福的康庄大道上。但是,如果你连让对象保持整洁都做不到的话,谈何可以写出优雅的代码呢?
尚未学会如何使用利器的程序员就不可能做出甜美的糕点。注意这里的用词:尚未。我相信每个人自己都会有一条成为 Ruby 和 Rails 称职程序员的道路,即使这条道路并不完全顺利。我说的称职是指:有足够的知识知道,何时以及如何根据不同的场景使用不同的工具,有时甚至危险的工具。
这不是在推脱帮助他们达成称职程序员的责任。语言和框架应该是有耐心的导师,愿意帮助和指导每个人成为专家。唯一可靠的道路就是不断犯错:错误地使用工具,些许血泪教训。没有捷径。
Ruby on Rails 是大厨以及那些想要成为大厨的人的厨房。也许刚开始你只是洗洗碗,但可以逐渐成长,最终可以运营整个厨房。在这个过程中,别相信任何人告诉你的:你无法驾驭业内最好的工具。
面向综合应用
Rails 可用于许多场景,但最初目的是创建一个整合系统:Majestic Monolith!即一个可以解决所有问题的完整系统。这就意味着 Rails 需要考虑从前端需要即时更新的 JavasScript 到生产环境下数据库如何进行版本迁移的所有细节。
如上所述,这确实是一个非常宽广的领域,但尚在我力所能及的范围之内。Rails 会专门寻找全栈工程师来创建这个整合系统。当然,目的不是将那些专注于某个领域的专家排除在外,而是因为整个全栈的团队可以更好的创建具有长远意义的事物。
正是专注于提高个体程序员的开发能力,让我们想要创建这样一个整合系统。在整合系统里,我们可以拿掉很多不必要的抽象,减少各层之间的重复(就像服务端和客户端共享的模版),最重要的是可以避免让应用成为分布式系统(在确实有必要之前)。
许多系统开发的复杂性来自于引入组件之间的新边界,这些边界限定了组件之间相互调用的方式。对象之间的本地调用要比远程微服务之间的调用简单得多。那些冒险启用分布式系统的人会发现,这是一个充满失败调用、网络延时和依赖更新周期的全新地狱。
有时候这种分布仅仅是简单的需求。比如你为 web 应用创建了一个 API 接口,以供其他人通过 HTTP 调用,那你就乖乖等着收拾烂摊子吧(尽管处理请求要比发送请求要容易的多。因为在发送请求时,别人的错误也会导致你自己的错误)。但对你自己来说,这至少不是一次愉快的开发经历。
更糟的情况是,系统可能过早地被拆分成了服务,甚至是更差的微服务。这通常来自于一个错误的认知:如果你想要个现代网络应用的话,只需要简单地多次的构建系统,一次在服务端,一次在基于 JavaScript MVC 的客户端,以及每个移动端的本地应用等。这不是基本规律,也完全没有必要。
其实完全可以在多个 app 间共享整个应用的大部分内容。相同的 Controller 和 View 可以用于桌面 web 应用,也可以内嵌到手机 app 的本地应用中。也就是尽可能将开发集中在同一个整合系统中。
而且这样做完全不必牺牲速度、用户体验或其他任何误导你过早拆分系统的因素。
这就是我们一直追寻的大一统:可自行调配、适用于多平台应用开发、易于理解和使用的单个整合系统。
演进优于稳定
当一个系统存在已经超过10年,如 Rails,一般都会自然僵化。每一处修改都有各种可能给他人带来灾难,因为他们可能依赖于过去的实现。而且,对这些人来说,他们的要求完全是合理的。
但如果我们太过迁就保守派的意见,我们将永远看不到事情的另一面。因此,我们必须偶尔打破陈规,以改变事情演进发展的方式。正是这种演进方式才让 Rails 得以存活,并可能在未来(数)十年继续蓬勃发展。
永远都是说起来容易做起来难,特别是当你自己的应用因 Rails 的重大版本升级(不向下兼容)而崩溃时。然而正是这些时刻,才让我们谨记 演进优于稳定 的价值所在,并为我们带来修复崩溃应用的力量,从而继续保持与时俱进。
这并不代表我们就可以不管三七二十一地给别人带来不必要的或过份的伤害。Rails 2.x 到 3 的重大升级就是这样一个艰难的决定,它所带来的噩梦还依然徘徊在那些亲身经历过的人的心头,经久不散。它让许多人在 2.x 版本停留了很长一段时间,有些人甚至对此深恶痛绝到失去了理智。但,从大局来看这个决定还是值得肯定的。
这些就是我们一直要做的艰难抉择。Rails 是否会因为今天的改变而在未来五年内变的更好?Rails 是否会因为接纳其他解决方案,如异步任务队列或 WebSocket,而变的更好?如果答案是肯定的,那就啥都别说了,让我们卷起袖子干活吧。
其实这项工作并不仅限于 Rails 本身,而应该在广大 Ruby 社区推行。Rails 应该站在帮助 Ruby 进步的前沿,推动广大社区成员尽快使用最新版本。
就目前情况来看,我们做的还不错。从我开始实践这个信条开始,我们已经经历 Ruby 1.6、1.7、1.8、1.9、2.0、2.1、2.1、2.2,直到目前最新的2.3。一路走来伴随着许多重大的变迁,但 Rails 始终是 Ruby 的坚强后盾,它帮助每个人尽快地适应新版本的变迁。这是 Rails 作为Ruby 主要推广者的部分权力和义务。
同样,这一点也适用于工具链上的其它辅助工具。Bundler 曾经是一个充满争议的理念,但是经过 Rails 的不懈努力的推广,它已经成了可以和 Rails 相提并论的基石,并成为人们理所当然的选择。像 Asset Pipeline 和 Spring 这样的连续处理指令组件其实也一样。这三个组件全都经历过或正在经历演进的痛苦,但是这些工具的长远意义促使我们一直推动他们前进。
最终,进步还是要取决于人以及他们想要做出改变的意愿。这就是为什么在 Rails Core 或 Rails Commiters 类似的小组中没有终身职位一说。这两个小组的成员都是那些为推动框架进步而积极工作的人。有些人可能只会坚持几年,不管怎样我们会永远感谢他们做出的贡献,但另外一些人可能会为之持续数十年。
因此,我们会一直欢迎和鼓励新的成员加入社区,这对我们来说至关重要。我们需要新的血液和理念来激荡出更好的进步。
兼收并蓄
Rails 因包含诸多富有争议的理念而闻名。如果我们要求每个人无时无刻遵循所有的信条,那么 Rails 将会很快沦为孤芳自赏的小群体。所以我们绝不会这样做。
我们需要不同的意见。我们需要不同的语法。我们需要多样的思想和成员。在这个思想的大熔炉中,我们可以提炼出最好的精华部分给大家分享。在此过程中,许多人以代码或深思熟虑的论点贡献了他们的意见。
这篇信条其实描述的是一种理想状态,日常生活中的实际情况要微妙(也更有趣)的多。Rails 可以在同一个帐篷里容纳下如此庞大的一个社区,究其原因是 Rails 很少或者根本没有试剑石(非一即二的考验)。
RSpec(一个我经常表达不满的测试DSL框架)的持续成功就是一个完美的佐证。尽管我可以为此吐槽到口干舌燥:为什么我觉得这不是正确的测试方式。但依然不能妨碍它大获成功。这点才是最为至关重要的。
Rails API 的出现同样如此。尽管我个人是关注和倾力的重点是带有 View 的整合系统,但对那些想要做前后端分离的人来说,毫无疑问这是 Rails 可以改进的地方。因此,我们应该欣然接受 Rails API 成为 Rails 的第二使命,而且我相信它配得上如此重要的地位。
当然,拥有同一个帐篷并不意味着需要迎合所有人的所有需求。它仅仅意味着欢迎所有人加入这个大家庭,并带来他们自己的想法。我们不必牺牲我们的灵魂和价值观来让他们加入我们,我们只是知道如何将不同的想法混合在一起,来产生新的美妙思想。
当然这样一个帐篷不是唾手可得的,还需要很多工作来让它受到大家的欢迎,特别是你的目标不仅仅是吸引那些已经在社区里的人。因此,降低入门门槛总是我们需要慎重考虑的工作首选。
你永远不会知道:修正文档中拼写错误的人是否最终可能会创作出下一项伟大功能。但是,你有机会向每个做出微不足道贡献的人表示感激,从而激励他们继续努力工作。