Rails 信條(轉(zhuǎn))

The Rails Doctrine

總結(jié)一下:

  • 程序員的幸福最大化
  • 約定優(yōu)于配置
  • 主廚精選
  • 多元化的設(shè)計(jì)模式
  • 推崇優(yōu)美的代碼
  • 提供實(shí)用工具
  • 重視整合系統(tǒng)
  • 進(jìn)步比穩(wěn)定重要
  • 包容并重

Ruby on Rails 驚人的活躍度主要是因?yàn)楸旧聿捎昧诵路f的技術(shù)和好的時(shí)機(jī)。但技術(shù)優(yōu)勢(shì)隨著時(shí)間的推移而減弱;縱然有好的時(shí)機(jī),時(shí)間久了影響也會(huì)逐漸減少。Rails 為何不僅能繼續(xù)存在,還依然具有影響力并帶領(lǐng)社區(qū)繼續(xù)前進(jìn)呢?需要更進(jìn)一步的解釋。我提議以下這篇內(nèi)容,這是我們的基本信條,也是飽受爭(zhēng)議的信條。

這篇信條,在過(guò)去 10 年里不斷演進(jìn),是 Rails 主要的支柱,同時(shí)也是基本的支柱。我不自詡自己是這些想法的原創(chuàng)者。Rails 的主要成就是整理和培養(yǎng)出一套編程與程序員本身的思想集合。

廢話不多說(shuō),以下是由我所認(rèn)為,Rails 中最重要的 9 個(gè)基本信條:

程序員的幸福最大化

是 Ruby 造就了 Rails,所以第一條信條便是從創(chuàng)造 Ruby 的核心理念所提煉出來(lái)。

早期 Ruby 的極端邪說(shuō)就是把程序員的幸福度放到第一位。還把追求幸福置于驅(qū)動(dòng)編程語(yǔ)言與生態(tài)圈前進(jìn)的考量之上。

然而 Python 可能對(duì)于“用一種方法,最好只有一種方法來(lái)完成一件事”而感到自豪,而 Ruby 則喜歡自身表現(xiàn)力與巧妙。Java 是飽受軟件工程師的強(qiáng)力推崇,Ruby 則在歡迎工具里就附上了自盡的繩子。Smalltalk 專注與消息傳遞的純粹性,Ruby 則累積關(guān)鍵字和臃腫的語(yǔ)法構(gòu)造。

Ruby 與眾不同的原因是看重的事情不一樣。這些考量,都是為了滿足和追求軟件工程師的幸福。這些追求導(dǎo)致了與其他編程語(yǔ)言的辯論,也打開了主流文化對(duì)于究竟什么是軟件工程師,以及應(yīng)該如何應(yīng)對(duì)軟件工程師的認(rèn)知。

Ruby 不僅承認(rèn),而且從設(shè)計(jì)上適應(yīng)和提升軟件工程師的感受。不管它們是不足的、奇思妙想的,還是令人喜悅的。Matz 跨越了驚人難度的實(shí)踐門檻,讓機(jī)器面有喜色,且富有人性。Ruby 滿滿是視覺上的錯(cuò)覺,在我們看起來(lái) Ruby 很簡(jiǎn)單,清晰,也很優(yōu)美,背后其實(shí)是雜技般的錯(cuò)綜復(fù)雜。這些選擇不是沒有代價(jià)(問(wèn)問(wèn) JRuby 那些試著要對(duì) Ruby 逆向工程的人看看?。?,這也是為什么,這是很值得贊揚(yáng)的一件事。

這是對(duì)軟件開發(fā)另一種愿景的致敬,也決定了我對(duì) Ruby 的鐘愛。這不止是簡(jiǎn)單易用,不僅是美學(xué)的元素,也不是單一的技術(shù)成就。而是一種愿景,是反文化。Ruby 是一個(gè)不適應(yīng)呆板專業(yè)軟件開發(fā)的人,而是專屬于愛好之士的樂土。

我過(guò)去談過(guò),發(fā)現(xiàn) Ruby 像是找到完全適合我的魔法手套。比我想象中的任何手套都來(lái)得合用。這是我從“寫程序只是因?yàn)槲倚枰绦颉钡健皩懗绦蚴且驗(yàn)闊嶂杂谄渲械男闹潜磉_(dá)與練習(xí)”的一次轉(zhuǎn)變。是找到無(wú)我之境,且能夠隨意開放的入口。對(duì)于任何熟悉 Csikszentmihalyi 作品的人來(lái)說(shuō),我真的一點(diǎn)也不夸張。

當(dāng)我說(shuō) Ruby 改變了我,我真的不夸張,Ruby 決定了我人生的努力目標(biāo)。如此深刻的啟示。Ruby 感染了我,呼喚我對(duì) Matz 的產(chǎn)物做出宣教的工作。傳播這深刻的語(yǔ)言與恩賜。

我想你們現(xiàn)在大概都感到疑惑的搖搖頭。我不怪你們,假如我還處于“編程只不過(guò)是工具”的階段,有人跟我說(shuō)上面的經(jīng)歷,我也會(huì)搖頭。接著我還可能會(huì)對(duì)這宗教般的語(yǔ)言發(fā)笑。但說(shuō)實(shí)在的,這也是最真誠(chéng)的理由,即便這可能使某些人,甚至是多數(shù)的人感到不愉快。

無(wú)論如何,究竟這對(duì) Rails 意味著什么?這個(gè)理念為什么持續(xù)引導(dǎo) Rails 演進(jìn)?要回答這個(gè)問(wèn)題,我想用另一個(gè)啟發(fā)性的理念來(lái)說(shuō)明,一個(gè)早期常用來(lái)描述 Ruby 的概念:最小驚訝原則(The Principle of Least Surprise)。Ruby 應(yīng)該要如你預(yù)期般的運(yùn)行。可以用下面這個(gè)對(duì)比于 Python 的例子來(lái)解釋:

$ irbirb(main):001:0> exit
$ irbirb(main):001:0> quit

$ python>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit

Ruby 可以用 exit 和 quit ,來(lái)回應(yīng)程序員的需求,也就是想離開終端交互界面。而 Python 則迂腐的告訴程序員如何做該做的事,即便它已經(jīng)知道程序員想干嘛了(卻只顯示錯(cuò)誤信息)。這是非常清晰的、微小的、最驚訝原則的例子。

不過(guò)最小驚訝原則最終在 Ruby 社區(qū)失寵的原因非常直觀。最小驚訝原則,驚訝誰(shuí)?那當(dāng)然是 Matz 了,以及那些跟 Matz 一樣對(duì)同樣事物感到驚訝的人。Ruby 社區(qū)成長(zhǎng)茁壯的同時(shí),人們對(duì)不同的事情感到驚訝與 Matz 成長(zhǎng)過(guò)程中感到驚訝的事情不一樣,這主要咎于失敗的郵件列表討論。為了要避免更多甲男對(duì)乙物是否感到吃驚的爭(zhēng)論沒完沒了,所以這個(gè)理念從此退居幕后。

再一次,這跟 Rails 到底有什么關(guān)系?嗯,Rails 就是按照相似的理念,盡量不驚訝(Matz)而設(shè)計(jì),以及(DHH 的)璀璨微笑理念,這理念就是:框架的接口設(shè)計(jì)是著重考量,怎樣可以讓人用起來(lái)能會(huì)心一笑。我寫下此句時(shí),即便是我自己,也覺得這在第一次聽到時(shí)候會(huì)感到非常的滑稽與自戀。

但創(chuàng)作出 Ruby 或 Rails 這樣的成果,本身就是深度自戀的努力成果。Ruby 與 Rails 都是誕生于單一創(chuàng)造者。但也許我只是在這把我的動(dòng)機(jī)投射在 Matz 身上,所以讓我縮小我宣言的范疇:我發(fā)明 Rails 只是為了我自己。從最初開始就是為了讓我自己微笑。Rails 的許多工具,很多方面看來(lái),它們的用途都是為了讓我更加享受人生。充實(shí)那些每天為需求所爭(zhēng)論不休的生活,以及用來(lái)打造 Web 應(yīng)用程序。

和 Matz 一樣,我有時(shí)候?yàn)榱藢?shí)現(xiàn)我的理念也會(huì)做出一些蠢事。一個(gè)例子便是 Inflector,一個(gè)可以對(duì)英文做不規(guī)則轉(zhuǎn)化的類,譬如 Person 類對(duì)應(yīng)到 People 表、Analysis 對(duì)應(yīng)到 Analyses,Comment 對(duì)應(yīng)到 Comments 等。這個(gè)東西現(xiàn)在已經(jīng)是 Rails 不可分割的元素了,但早期爭(zhēng)議的怒火延燒到今日,傷口仍未愈合,由此可見其重要性。

另一個(gè)例子僅用了些許代碼實(shí)現(xiàn),卻幾乎引發(fā)了驚愕的程度。

Array#second 到 #fifth(以及挑釁意味的 #forty_two )。這些別名的存取器,非常嚴(yán)重的冒犯了常發(fā)表意見的支持者,他們說(shuō):這簡(jiǎn)直太過(guò)度設(shè)計(jì)了(幾乎是編程時(shí)代的結(jié)束),這些寫成 Array#[1]、 Array#[2](以及 Array[41])不就可以了嘛。

但時(shí)至今日,主要的抉擇還是,讓我自己開心。我喜歡在終端或測(cè)試?yán)锞帉?people.third
。不,這不合理,也不高效??赡芪矣胁“?,但這仍能讓我發(fā)自內(nèi)心的微笑,滿足了這個(gè)理念,也豐富了我的人生,幫我在過(guò)了 12 年之后,還仍繼續(xù)參與 Rails。

不像性能優(yōu)化有明確的指標(biāo),幸福優(yōu)化很難衡量。這使得幸福優(yōu)化變成了不科學(xué)的無(wú)謂努力,某些人要不是徹底失望,可能就是覺得這并不怎么重要。程序員被教導(dǎo)要爭(zhēng)執(zhí)并征服實(shí)際的東西,也就是可以明確指出,為什么 A 比 B 好的東西。

但追求幸福,是很難從微觀的角度來(lái)衡量,但從宏觀的角度來(lái)看便很清楚。許多人留在 Ruby on Rails 社區(qū)便是這個(gè)原因。他們自豪于能更好、更圓滿的工作生活。而正是因?yàn)檫@些情感因素,勝利是很清楚的。

總結(jié),追求幸??赡苁切纬?Ruby on Rails 的關(guān)鍵因素。而這個(gè)因素將會(huì)跟著 Rails 一直走下去。

約定優(yōu)于配置

早期 Rails 的生產(chǎn)力座右銘是:“你不是獨(dú)一無(wú)二的,大家都一樣”。只要放下了自負(fù)的個(gè)人喜好,便可以跳過(guò)無(wú)謂的世俗決定,專注在最重要的地方下更快的決定。

有人在乎你的數(shù)據(jù)庫(kù)主鍵命名用什么格式嗎?用 id、 postID 、 posts_id或 pid 真的有差別嗎?這真的是值得反復(fù)討論的嗎?不!

Rails 的部分使命就是,幫助開發(fā)者在日漸復(fù)雜龐大的 Web 應(yīng)用程序決策叢林中劈荊斬棘。幾千個(gè)無(wú)謂的決定只需要做一次,而若是別人幫你做這些決定,那就更好了。

約定優(yōu)于配置,可以將我們從各種小的決定中解放出來(lái),也提供一個(gè)繁茂的草原,讓我們?cè)杏顚拥某橄蟆H绻覀兛梢砸蕾?Person 類對(duì)應(yīng)到 people 表,我們也可以用同樣的詞形變化,從 has_many :people 推導(dǎo)出類名是 Person。優(yōu)良約定的力量是廣泛使用才能獲得許多好處。

不僅專家的生產(chǎn)力提升了,約定也降低了新手的門檻。Rails 里有非常多的約定,新手無(wú)須知道,但卻能實(shí)實(shí)在在的從中獲益。他們不需要了解每一件事情為什么是這樣,也可以打造出偉大的應(yīng)用。

但框架要僅是厚重的教科書就不可能了,新的應(yīng)用好比一張白紙。單純要理解到從那開始,如何起步,就需要花費(fèi)巨大的努力。一半的時(shí)間都花在糾結(jié),糾結(jié)該拉那一條線才是正確的。

同樣的道理也可以套用在當(dāng)你了解了所有組件是如何一起工作時(shí)。每當(dāng)一個(gè)改變的下一步非常明確時(shí),我們可以快速的游走在應(yīng)用相似或于其它應(yīng)用相同的組件之間。一個(gè)包含了所有東西的地方,所有東西都在一個(gè)地方。即便是最有能的人,也可以從約束中解放出來(lái)。

世間萬(wàn)物,有好有壞,約定的力量不是沒有危險(xiǎn)。當(dāng) Rails 這么簡(jiǎn)單就可以做這么多事情的時(shí)候,很容易就會(huì)這么想,應(yīng)用的每個(gè)部分,都可以用一個(gè)預(yù)先準(zhǔn)備的模版來(lái)解決。

但多數(shù)值得做的應(yīng)用,總有某種程度上獨(dú)一無(wú)二的部分,它們可能是 5% 或是 1%,不管多少,但總是會(huì)有的。

最困難的部分就是何時(shí)應(yīng)該打破約定?什么時(shí)候才要偏離正軌?我主張多數(shù)的行動(dòng)其實(shí)都沒什么不同的,也沒好好認(rèn)真想過(guò),而偏離 Rails 的代價(jià)被低估了,其實(shí)是有代價(jià)的,需要你引以深刻的反思。

主廚精選

在餐廳不知道什么好吃該怎么點(diǎn)菜?若你交給主廚挑選,可能可以在了解為何“好”之前,就吃上一頓好菜。這就是 Omakase。無(wú)需成為美食專家或亂點(diǎn)菜碰運(yùn)氣,就可以吃到好東西的方法。

對(duì)軟件開發(fā)來(lái)說(shuō),主廚精選這個(gè)實(shí)踐的好處是技術(shù)棧交給別人幫你組合,跟我們從約定優(yōu)于配置所得出的論點(diǎn)相似,但它更上一層。CoC (約定優(yōu)于配置)考量如何用好單一的工具,而 Omakase 則考量該用什么框架,以及框架之間該如何協(xié)作。

有這么一說(shuō),軟件開發(fā)的優(yōu)良傳統(tǒng)便是把可用的工具交給使用者自己去選擇,決定權(quán)(還是負(fù)擔(dān)?)交給程序員。

你一定聽過(guò)這句話,也在心里面點(diǎn)頭贊同:“用最好的工具來(lái)解決問(wèn)題”。這聽起來(lái)很基本,但能夠選擇“最好的工具”需要明確的定義出什么是“最好”。這其實(shí)比想象中要難的多。

這個(gè)問(wèn)題跟在餐廳要吃什么相似。像是挑選八道菜的每一道,選擇每一個(gè)三方庫(kù)或框架,這不是獨(dú)立的決定。選擇需要根據(jù)今晚的狀況來(lái)考慮,挑選三方庫(kù)則要考慮整個(gè)系統(tǒng)。

所以 Rails 我們根據(jù)大局考慮,把選擇減少到一套好的工具,讓每個(gè)工程師可以從中挑選:一套為了大家好的工具。好處如下:

分散風(fēng)險(xiǎn) :當(dāng)大家都用預(yù)設(shè)的方式來(lái)使用 Rails,我們有共同的體驗(yàn)。這個(gè)共同基礎(chǔ)讓我們可以容易指導(dǎo)和幫助新人,大家有討論的共同基礎(chǔ)。就好比大家昨晚七點(diǎn)都看了某個(gè)節(jié)目,所以明天大家都有共同話題可以談?wù)摗_M(jìn)而培養(yǎng)出更有凝聚力的社區(qū)。
大家熟練使用相同的工具 :身為一個(gè)全棧的框架,Rails 有很多可以移動(dòng)的組件,這些組件之間如何協(xié)調(diào)工作?如何獨(dú)立工作??jī)烧咄瑯又匾?。多?shù)的軟件開發(fā)者的痛苦不是來(lái)自于獨(dú)立的組件,而是組件之間的互通。當(dāng)我們用同樣的方式來(lái)使用這些組件,相同的配置便可以得出相同的錯(cuò)誤,則大家受的苦便減少了。
有必要的時(shí)候也可以換掉 :Rails 是主廚精選的技術(shù)棧,某些子框架或類庫(kù)你仍然有選擇的權(quán)利。只是不一定你要換掉它們,這些決策可以之后再做,在你開發(fā)出一個(gè)明確的、需要不一樣的個(gè)性化應(yīng)用的時(shí)候再來(lái)考慮吧。

因?yàn)槎鄶?shù)使用 Rails 且有經(jīng)驗(yàn)的軟件工程師,并不是都討厭菜單上的每一道菜(討厭的話就不會(huì)繼續(xù)使用 Rails 了)。他們努力挑選想要的替代方案,而其它則享受和大家一樣的工具。

多元化的設(shè)計(jì)模式

大家對(duì)中央集權(quán)的概念有很強(qiáng)烈的情感,而根據(jù)最終得出的理性結(jié)論來(lái)作為架構(gòu)基礎(chǔ)。這個(gè)理念存在某種純粹性,這也是為什么軟件工程師會(huì)被中央集權(quán)所吸引。

Rails 不是這樣,Rails 不是一件裁剪完美的衣服,Rails 是一件棉被。一個(gè)用很多概念,甚至是設(shè)計(jì)模式組成的棉被。這些概念和設(shè)計(jì)模式看起來(lái)可能有抵觸、自相矛盾或互相矛盾。但這不是我們要做的事,這不是靠有更優(yōu)越的點(diǎn)子就可以勝出的總冠軍賽,不是一定要選出一個(gè)贏家。

看看 Rails MVC 里用來(lái)打造 View 的模版。在默認(rèn)情況下,這些 Helper 允許我們從 View 抽取出代碼,抽出來(lái)不過(guò)是一堆函數(shù)!這些函數(shù)甚至存在于同一個(gè) Namespace 下面。噢,驚訝了嗎?感到恐懼了嗎?正是像 PHP 那樣呢!

但我認(rèn)為 PHP 在單一函數(shù)之間,很少需要互動(dòng)這點(diǎn)上事對(duì)的,就像 View 有著許多的抽象一樣。根據(jù)這個(gè)目的,單一的 Namespace,一堆的方法,這不只是個(gè)合理的決定,還是很棒的決定。

這不代表我們不會(huì)偶爾想要用“面向?qū)ο蟆钡姆绞絹?lái)打造 View。Presenter 的概念,把彼此之間獨(dú)立的方法以及要呈現(xiàn)的資料包在一起;這可能是方法混在一起最好的解藥。但這很少見。

相比之下,我們通常把 MVC 的 Model 看成是“面向?qū)ο蟆本A的主要堡壘(需要改善翻譯!)。選擇正確的名稱,增加一致性,減少耦合,這是 Model 好玩的地方。這跟 View 是很不一樣的,所以我們采用了和 View 不同的做法。

但即便我們不采納單一設(shè)計(jì)模式的教條。Rails 的 concerns,Ruby mixin 的特例,通常用來(lái)擴(kuò)展 Model。這跟 Active Record 的模式完美結(jié)合,給予每個(gè)方法有直接存取、存儲(chǔ)正在交互資料的權(quán)力。

即使 Active Record 框架的根本侵犯了某些純粹主義者。我們把跟數(shù)據(jù)庫(kù)打交道的邏輯和業(yè)務(wù)邏輯混合了在一起。合并了邊界!沒錯(cuò),因?yàn)檫@才是做出 Web 應(yīng)用貼切的實(shí)際方法, Web 應(yīng)用通常就需要跟數(shù)據(jù)庫(kù)打交道,也需要存儲(chǔ)某些業(yè)務(wù)邏輯的狀態(tài)。

有如此理想的彈性,正是為什么 Rails 可以解決許多不同的問(wèn)題。多數(shù)單一的流派解決單一的問(wèn)題很厲害,但超出舒適圈就變得非常尷尬死板,需要使用許多交錯(cuò)的設(shè)計(jì)模式。而我們旁敲側(cè)擊,在后面把關(guān),最終整合出來(lái)的框架更強(qiáng)大,也是比單一模式能做更多事情。

現(xiàn)在,這種與許多程序設(shè)計(jì)流派結(jié)合的多重關(guān)系,是概念上的負(fù)擔(dān)。僅了解面向?qū)ο蟪绦蛟O(shè)計(jì)是不足以用好 Rails 的。還需要有面向過(guò)程編程和函數(shù)式編程的經(jīng)驗(yàn)。

這也可以應(yīng)用到許多 Rails 的子語(yǔ)言。我們不試圖剝奪你學(xué)習(xí)的機(jī)會(huì),比如你可以在 View 里寫 JavaScript,或者用 SQL 來(lái)寫復(fù)雜的查詢。這些都是允許的。

降低學(xué)習(xí)曲線的方法,真的只是想辦法讓大家更容易上手,在了解框架的每一個(gè)組件之前,做一些有實(shí)際價(jià)值的東西(更有意義)。這也是框架為何要盡快示范出 Hello World。告訴你桌子已經(jīng)準(zhǔn)備好了,前菜也上了。

這其中的思想就是:盡早給出有實(shí)際價(jià)值的東西。我們鼓勵(lì) Rails 的實(shí)踐家快速學(xué)習(xí),接受學(xué)習(xí)的過(guò)程是一種喜悅,而不是障礙。

推崇優(yōu)美的代碼

我們寫程序,不只是要讓計(jì)算機(jī)和其他人理解,而是要沐浴在優(yōu)美的夕陽(yáng)余暉里。看起來(lái)漂亮的代碼,本身就有價(jià)值的,要盡力追尋。但這不代表優(yōu)美的代碼就應(yīng)該勝于一切的考量,而是要把優(yōu)美納入優(yōu)先考量。

什么是優(yōu)美的代碼?在 Ruby 里面通常穿插于 Ruby 本身的慣用式和自定義的 DSL 威力之間。這是一條模糊的分界線,但非常值得一試。

以下是一個(gè)取自 Active Record 的簡(jiǎn)單例子:

class Project < ApplicationRecord 
    belongs_to :account 
    has_many :participants, class_name: 'Person' 
    validates_presence_of :name
end

這看起來(lái)像是 DSL,但這只是一個(gè)類 (Class) 定義,其中有三句類方法 (Class Method) 調(diào)用,方法調(diào)用接收 Symbol 以及 Hash 參數(shù)。沒有什么特別的,但這很漂亮。這也很簡(jiǎn)單。只需幾行聲明,變賦予我們所大的能力與彈性。
這幾條語(yǔ)句部分的美麗,來(lái)自于履行先前的理念,像是約定優(yōu)與配置。當(dāng)我們使belongs_to :account是,我們假設(shè) projects表又一個(gè)叫做 account_id的外鍵存在。當(dāng)我們把 participant 的關(guān)聯(lián)類用 class_name,指定為 Person 時(shí),我們只需要定義 Person
類就可以了。這條語(yǔ)句便可推導(dǎo)出外鍵以及其它需要的設(shè)定。

以下是取自數(shù)據(jù) Migration 的另一個(gè)例子:

class CreateAccounts < ActiveRecord::Migration 
    def change 
        create_table :accounts do |t| 
            t.integer :queenbee_id 
            t.timestamps 
        end 
    end
end

這是框架威力的精華所在。軟件工程師按照某種約定來(lái)聲明一個(gè)類,像是繼承自 ActiveRecord::Migration并實(shí)現(xiàn) #change方法,剩下的就交給框架來(lái)處理,該去哪里,該調(diào)用哪個(gè)方法。

這樣軟件工程師只需編寫很少的代碼。以 Migration 的例子來(lái)說(shuō),這樣一個(gè)文件加上 rails db:migrate命令,就可以把創(chuàng)建一張新表;反過(guò)來(lái)也可以用另一條命令來(lái)刪除一張表。這跟軟件工程師自己使用背后的函數(shù)去修改,搞定所有細(xì)節(jié)很不一樣。

有時(shí)候優(yōu)美的代碼反而更玄乎。不是要追求寫得多短多短,或是多厲害,而是讀起來(lái)要有節(jié)奏感。

以下兩條語(yǔ)句是等價(jià)的:

if people.include? person... 
if person.in? people

但它們的執(zhí)行流程與關(guān)注的地方有著微妙的不同。第一個(gè)語(yǔ)句,關(guān)注的是群體里是有有特定的人。第二條語(yǔ)句則是人是否屬于群體。兩條語(yǔ)句都差不多,但我主張第二條語(yǔ)句更美,能讓我在想個(gè)條件的時(shí)候更容易有種會(huì)心一笑的感覺。

提供實(shí)用工具

Ruby 本身就有許多實(shí)用的工具,不是碰巧,設(shè)計(jì)正是如此。最出名的就是 Monkey Patching - 一種可以修改類與方法的能力。

但這個(gè)能力很快被嘲諷說(shuō),一般的軟件工程師掌控不了。而限制性較為嚴(yán)格的語(yǔ)言陣營(yíng)的人們認(rèn)為,Ruby 這個(gè)功能盲目的信任軟件工程師,Ruby 注定是要失敗的。

什么都可以改變的話,又怎么能阻止你把 String#capitalize從原來(lái)處理 "something bold".capitalize改成 "Something Bold"而不是原來(lái)的 "Something bold"呢?這里你自己的應(yīng)用上可能沒問(wèn)題,但這就把其它依賴 String#capitalize的庫(kù)搞壞了。

有人說(shuō)禁止使用才是答案。在 Ruby 里面,只要有好的理由,沒有什么可以阻止你使用 Monkey Patching。我們透過(guò)約定、推廣、教育來(lái)宣導(dǎo)好的觀念,而不是禁止使用廚房的菜刀或者規(guī)定每個(gè)人只能用湯勺來(lái)切番茄。

但 Monkey Patching 的另一面是能夠做成 2.days.ago (算出從現(xiàn)在計(jì)算兩天前的時(shí)間)這樣的美技。你可能會(huì)覺得,哦,這交易真不劃算。也就是你寧可不要 2.days.ago
也不想修改語(yǔ)言的標(biāo)準(zhǔn)庫(kù),你若這么想的話,那 Ruby 可能不適合你。

但有些人出自于安全性的考慮,不得已只好放棄修改 Ruby 的標(biāo)準(zhǔn)庫(kù)。但反過(guò)來(lái)說(shuō),語(yǔ)言活躍的原因,正是因?yàn)榻o軟件工程師提供了激進(jìn)的觀點(diǎn):同時(shí)也相信可以放心的把工具交給軟件工程師使用。

不只是相信,語(yǔ)言本身也有多種方法來(lái)教導(dǎo)我們使用這些有用的工具。如此一來(lái)我們可以把整個(gè)產(chǎn)業(yè)向上提升,透過(guò)假設(shè)多數(shù)的軟件工程師都想要進(jìn)步,大家都能使用好的工具,而不是傷害到自己。這真是一個(gè)鼓舞人心的想法啊,但這有悖于大部分軟件工程師對(duì)其他同事的想法。

在決定實(shí)用工具的價(jià)值時(shí),總得以大家為出發(fā)點(diǎn)來(lái)討論。我尚未聽到任何一個(gè)軟件工程師說(shuō),“我無(wú)法相信這股力量,請(qǐng)把它拿走!”。常常聽到則是“我想其他的軟件工程師會(huì)濫用這個(gè)”。但我從來(lái)就不喜歡這個(gè)家長(zhǎng)式的作風(fēng)。

這正是 Rails 要提供實(shí)用工具的原因??蚣芩峁┑墓ぞ卟粌H和語(yǔ)言所提供的工具同樣實(shí)用,也很銳利。我們不為提供這些工具而感動(dòng)抱歉。事實(shí)上我們自豪,我們有足夠的信心啟發(fā)下一代的軟件工程師,并且有勇氣相信他們。

許多 Rails 的功能常常飽受這樣的爭(zhēng)議:“太過(guò)自由”。但我現(xiàn)在想到的一個(gè)例子是 Concern。這個(gè)基于 Ruby 內(nèi)建功能 Module 之上的一層,薄薄的語(yǔ)法糖。為了單一類打造,可以用來(lái)封裝多個(gè)相關(guān)卻又獨(dú)立理解的“關(guān)系”(也正是 Concern 名字的由來(lái))。

對(duì)于 Concern 的指控是有了一組新的抽屜,讓軟件工程師很容易把物件都塞進(jìn)去。這說(shuō)得沒錯(cuò),Concern 的用途就像這樣。

但謬論是不要提供像是 Concern 的功能,但凡讓有點(diǎn)能力的人來(lái)使用 Concern,便可得出有說(shuō)服力的概念分離,軟件工程師可以從 Concern 獲得先天上的架構(gòu)優(yōu)勢(shì)。這么說(shuō)吧,如果你不能保持 Concern 的整潔度,那你也不可能寫出優(yōu)雅的代碼。

尚未學(xué)會(huì)使用這些實(shí)用工具的軟件工程師,嘗不到收獲的果實(shí)。這里有一個(gè)重要的點(diǎn):“尚未”。我相信每個(gè)軟件工程師都有自己的一條道,最終都將變成有能力的 Ruby 與 Rails 軟件工程師。有能力我是指足夠的知識(shí),知道自己應(yīng)該在何時(shí),以及該怎么根據(jù)實(shí)際場(chǎng)景來(lái)使用不同的工具,有時(shí)甚至使用危險(xiǎn)工具。

這不是要把幫助他們成為厲害開發(fā)者的責(zé)任卸下??蚣芘c語(yǔ)言應(yīng)該是有耐心的導(dǎo)師,愿意幫助和指導(dǎo)任何人,讓他們走上大師之路。同時(shí)認(rèn)可不斷犯錯(cuò)是唯一的道路:錯(cuò)用工具、一點(diǎn)教訓(xùn)、汗水,有時(shí)候還可能是淚水。是的,想要變強(qiáng)就是應(yīng)該這樣,沒有捷徑。

Ruby on Rails 是大廚的廚房,是想變成大廚的試煉場(chǎng)。你可能從洗盤子起步,但可以一步步努力往上爬,爬到掌管廚房。別讓任何人告訴你,最好的工具不能放心的交給你實(shí)用,而是應(yīng)該看作成長(zhǎng)過(guò)程的一部分。

重視整合系統(tǒng)

Rails 可以在很多場(chǎng)景下使用,但最初是用來(lái)做整合系統(tǒng)的 Majestic Monolith !Majestic Monolith - 即用一個(gè)系統(tǒng)來(lái)解決所有的問(wèn)題。這表示著 Rails 從做即時(shí)刷新的前端 JavaScript,到能讓數(shù)據(jù)庫(kù)在生產(chǎn)環(huán)境遷移到下一個(gè)版本等等各方面功能,都要納入設(shè)計(jì)的考量。

我們已經(jīng)談過(guò),這是多么廣闊的眼界。但對(duì)于一人團(tuán)隊(duì)來(lái)說(shuō),不過(guò)就是現(xiàn)實(shí)的考量而已。Rails 特別尋找通才(全棧工程師,各方面都擅長(zhǎng)的人)來(lái)打造整個(gè)系統(tǒng)。目的不是要把專家束之高閣,而是讓通才和專家合力,打造出更具長(zhǎng)遠(yuǎn)價(jià)值的系統(tǒng)。

為了讓一個(gè)人可以做更多的事情,才想出了整合系統(tǒng)。而正是在整合系統(tǒng)里,我們可以拿掉許多不必要的抽象,減少抽象層之間的重復(fù)(像是服務(wù)端與前端可以共用 View 模版),以及避免系統(tǒng)在非必要的情況下分離。

系統(tǒng)開發(fā)的復(fù)雜度多半是引入了系統(tǒng)組件之間的界限,比如現(xiàn)在 A 組件該如何和 B 組件進(jìn)行相互調(diào)用。本地組件之間的方法調(diào)用要遠(yuǎn)比 Microservices 之間的遠(yuǎn)程調(diào)用來(lái)的簡(jiǎn)單。Microservices 是另一個(gè)存在失敗狀態(tài)、延長(zhǎng)問(wèn)題,依賴更新周期的新場(chǎng)景(需要對(duì)比原文理順,有點(diǎn)不太對(duì)),有許多潛在的問(wèn)題等著嘗試拆分的人去冒險(xiǎn)。

當(dāng)然有時(shí)候這種服務(wù)的拆分是必要的。若想建立讓大家可以透過(guò) HTTP 實(shí)用的 API,好吧,那你就得毫無(wú)怨言的處理許多的問(wèn)題(雖然處理進(jìn)來(lái)的請(qǐng)求比發(fā)出去請(qǐng)求要單一,但要是你的服務(wù)掛了,別人就會(huì)收到錯(cuò)誤的狀態(tài)了)。但這至少對(duì)你個(gè)人的開發(fā)體驗(yàn)傷害有限。

更糟糕的是,當(dāng)系統(tǒng)過(guò)早解耦,或是過(guò)早拆成小服務(wù),以及更糟糕的拆成 Microservices。這是現(xiàn)代網(wǎng)絡(luò)應(yīng)用的錯(cuò)誤認(rèn)知,你只會(huì)反復(fù)的重造系統(tǒng):同樣的功能在后端做一次,在前端再做一次,在 Native 手機(jī)端又再次實(shí)現(xiàn)一次等等。這不是自然規(guī)律,你也不需要這樣。

若想在整個(gè)應(yīng)用里共享大部分的功能,這完全是可行的。桌面應(yīng)用和 Mobile App 可以用同樣的 Controller 和 View。盡可能的把功能集中在 Majestic Monolith - 整合系統(tǒng)。集中在一起完全不需要犧牲構(gòu)建速度,開發(fā)者體驗(yàn),以及其它錯(cuò)讓你以為要盡早拆分系統(tǒng)的因素。

這正是我們要追求的系統(tǒng):包含所有功能,容易發(fā)布,簡(jiǎn)單理解的單一系統(tǒng),整合系統(tǒng)。

進(jìn)步比穩(wěn)定重要

當(dāng)一個(gè)系統(tǒng)已經(jīng)存在超過(guò) 10 年,比如 Rails,自然會(huì)慢慢僵化。每一處的修改,都有百萬(wàn)種理由可能會(huì)變成別人的困擾,或是有人仍需要舊的行為。這對(duì)他們來(lái)說(shuō)都是合理的理由。

但若我們太仔細(xì)聽取保守派的意見,我們將永遠(yuǎn)不知道另一邊的聲音是什么。我們需要勇敢的做出改變,打破陳規(guī),然后才可以茁壯演化。正是這樣的演變,才讓 Rails 存活下來(lái),并可能繼續(xù)繁榮發(fā)展(數(shù))十年。

這些理論上都很簡(jiǎn)單,但實(shí)際做起來(lái)卻很困難。特別是你的應(yīng)用因?yàn)樯?jí)大版本的 Rails 時(shí),因?yàn)橛胁患嫒莸淖兏鴫牡袅恕U谴藭r(shí),我們珍重[進(jìn)步比穩(wěn)定更重要]的價(jià)值觀,這個(gè)價(jià)值觀給我們力量來(lái)搞定升級(jí),搞清楚為什么并與時(shí)俱進(jìn)。

但這不代表我們就可以加入不需要的功能,或是胡來(lái)把別人的應(yīng)用搞壞了。Rails 2 升到 3 的遷移噩夢(mèng),仍在經(jīng)歷過(guò)的人身上陰魂不散。那真的是很艱難的決定。修改太大使得許多人停滯在 Rails 2.x 無(wú)法前進(jìn),有些人更是討厭至極??晌覀儚拇缶謥?lái)看,這件事仍然是值得的選擇。

這些是需要一直做出的權(quán)衡。做了這個(gè)改變之后,Rails 能夠在 5 年之內(nèi)變得更好嗎?Rails 可以采用某些問(wèn)題的解決方案,比如異步任務(wù)隊(duì)列或 WebSockets,這能讓 Rails 在今后變得更好嗎?如果這個(gè)答案是肯定的,那么就別說(shuō)了,卷起袖子干活吧。

這些工作不只是 Rails 需要,廣大的 Ruby 社區(qū)也需要。Rails 應(yīng)該站在時(shí)代的前沿,通過(guò)引導(dǎo)大家快速升級(jí)新版本來(lái)推動(dòng) Ruby 演進(jìn)。

這一點(diǎn)我們做的非常好。最早開始時(shí),我們從 Ruby 1.6、1.7、1.8、1.9、2.0、2.1、2.2 一路升上 2.3。沿路歷經(jīng)多次的大版本更新,但 Ruby 總是有 Rails 當(dāng)后盾,并幫助每個(gè)人的代碼跑得更快。這是身為最流行的 Ruby 項(xiàng)目 Rails 的特權(quán),同時(shí)也是義務(wù)。

這點(diǎn)也可以延伸到工具鏈的輔助工具。Bundler 曾是一個(gè)爭(zhēng)議性的概念,但透過(guò) Rails 的不懈努力,Bundler 成了未來(lái)的重要工具,今天理所當(dāng)然已經(jīng)是不可或缺的工具了。同樣也可以應(yīng)用到 Assets Pipeline 和 Spring (Rails 開發(fā)環(huán)境常駐的指令)。這三個(gè)一起出現(xiàn),也仍然一起往前走,過(guò)程中可能造成了一些痛苦,但長(zhǎng)遠(yuǎn)下來(lái),這些工具提供的價(jià)值正是為什么我們要推廣它們的原因。

最終進(jìn)步的是人,以及人們樂意做出改變。這也是為什么,像 Rails Core 或 Rails Committers 這樣的團(tuán)隊(duì)里沒有終身職位。這兩個(gè)團(tuán)隊(duì)是給活躍的,致力于帶動(dòng)框架進(jìn)步的那群人而設(shè)定。對(duì)某些人來(lái)說(shuō),可能就幾年,但我們永遠(yuǎn)感激他們做出的貢獻(xiàn),這些貢獻(xiàn)可能將持續(xù)影響我們幾十年。

而這也是為什么對(duì)我們來(lái)說(shuō),有一個(gè)能鼓舞社區(qū)新成員的友好環(huán)境非常重要。我們需要新血液、新概念,才可以激蕩出更好的進(jìn)步。

包容并重

正是歸功于這么多爭(zhēng)議性的概念才有 Rails。但若我們要求每個(gè)人完全遵從所有的信條,Rails 可能很快的就變成孤芳自賞的小族群,所以我們不這么做!

我們需要反面意見。我們需要方言。我們需要多元的思想和群眾。許多人用代碼或是深思熟慮的論證來(lái)提供意見。正是在概念的熔爐里,我們才能提煉出這之中最好的讓大家共享。

這篇基本信條描繪出一種理想狀況,但日常生活中的實(shí)際情況卻是更微妙的(也更有趣的),正因?yàn)?Rails 幾乎從來(lái)不考驗(yàn)(? 不合理)每一個(gè)想法,它才能包容下這樣龐大的社區(qū)RSpec,一個(gè)我不喜歡的 Test DSL,RSpec 的持續(xù)成功是最完美的證據(jù)。雖然我可以說(shuō)到臉紅氣喘,說(shuō)為什么我不覺得它的方式是對(duì)的,但 RSpec 仍可以繁榮綻放。這點(diǎn)才是最重要的!

同樣的論點(diǎn)也適用在 Rails 做 API 的例子。雖然我個(gè)人關(guān)注也花心思的是帶有 View 的整合系統(tǒng);但對(duì)于一些想要做前后端分離的人來(lái)說(shuō),這當(dāng)然是 Rails 可以改進(jìn)的地方。我們應(yīng)該要擁抱這一點(diǎn),就是 Rails 不只是能做一件事情,當(dāng)然也可以做 API。

包容并重并不代表把所有東西強(qiáng)加給所有人。這只是代表你歡迎人們帶自己的飲料來(lái)參加你的 Party。我們要給別人加入我們的機(jī)會(huì),卻又不能失了我們的靈魂與價(jià)值觀。因?yàn)槲覀儚乃麄兩砩峡梢詫W(xué)到如何調(diào)一兩杯新的飲料。

當(dāng)然這是要付出代價(jià)的,需要努力讓付出的貢獻(xiàn)受歡迎。特別是我們目標(biāo)不僅僅是要吸引住已經(jīng)在社區(qū)的人,我們永遠(yuǎn)都要真正考慮如何降低起步門檻。

你永遠(yuǎn)不知道,改正文件錯(cuò)誤拼寫的人,可能就是下個(gè)大功能改進(jìn)的黑馬。無(wú)論是多小的貢獻(xiàn),都能笑著說(shuō)感謝,這樣貢獻(xiàn)者就能一直持續(xù)下去。

轉(zhuǎn)自:http://www.tuicool.com/articles/Z7BfInM
英文版:http://rubyonrails.org/doctrine/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近在 Ruby China 看到 @huacnlee 分享的 The Rails Doctrine - 中文翻譯...
    freefishz2閱讀 1,149評(píng)論 0 6
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評(píng)論 25 709
  • 碼字的時(shí)間先生前幾天找我咨詢,問(wèn)了這樣一個(gè)問(wèn)題:“田寶,我有著幾十年的行業(yè)經(jīng)驗(yàn),語(yǔ)言也較為流暢,寫出來(lái)的都是原創(chuàng),...
    田寶談寫作閱讀 7,617評(píng)論 60 392
  • 金南俊 你今天難得做了一桌飯,等他回來(lái),這讓他有點(diǎn)不習(xí)慣,他吃著飯,一抬頭就看見你看著他,眼里發(fā)出...
    牽絆余生系列閱讀 180評(píng)論 0 0
  • 已經(jīng)是十多年前的回憶了。 去到德國(guó)時(shí)已是四月份,想四月份該春暖花開了,為了少帶行李,最厚的衣服是一件小絲棉襖。結(jié)果...
    木蘭11閱讀 303評(píng)論 0 0

友情鏈接更多精彩內(nèi)容