技術(shù)總監(jiān)為什么也會(huì)選錯(cuò)技術(shù)架構(gòu)?
我叫 Dan McKinley,坑里的那個(gè)人就是我。
我現(xiàn)在在一家叫作 Mailchimp 的公司上班。更早之前是在 Etsy,因?yàn)樵?Etsy 待的時(shí)間比較長,所以后面會(huì)更多地提到我在這家公司的經(jīng)歷。其實(shí)在離開 Etsy 之后,我也在其他幾家公司干過。
我既在大公司待過,也在小公司待過,還創(chuàng)辦過自己的公司。在經(jīng)歷了這些公司之后,我注意到了一些現(xiàn)象。
大公司有自己的做事方式,他們提供了“沙盒”一樣的環(huán)境,在這樣的環(huán)境里,會(huì)有人滿足你的需求,幫你答疑解惑,讓你感覺受到了“百般寵愛”。
但我也經(jīng)歷過幾個(gè)過渡時(shí)期,在這些過渡時(shí)期,需要自己解決一些棘手的問題。
首先,如何選擇合適的技術(shù)?
另一個(gè)我比較關(guān)心的問題是:如何讓開發(fā)人員開心地使用這些技術(shù)?因?yàn)槲易约阂彩情_發(fā)者,所以這一點(diǎn)對(duì)于我來說比較重要。如果有可能,我會(huì)盡量讓自己過得開心些。
如果你問開發(fā)人員什么東西會(huì)讓他們開心,他們通常會(huì)說:“如果可以使用 Clojure 作為開發(fā)語言,我就會(huì)很開心”。我不否認(rèn),當(dāng)他們說這些話的時(shí)候,他們腦子里浮現(xiàn)的應(yīng)該是曾經(jīng)最讓他們感到興奮的經(jīng)歷。
但我相信他們所描述的這種狀態(tài)是他們所能達(dá)到的最高的精神境界。
我以前也喜歡這樣。
例如,Etsy 的早期應(yīng)用程序是用 PHP 開發(fā)的,而開發(fā)這些應(yīng)用程序的人當(dāng)時(shí)剛好在學(xué)習(xí) PHP。
但我卻花了好幾年時(shí)間盡量不去碰觸這些 PHP 代碼,我甚至嘗試使用 Scala 和 MongoDB 來重新開發(fā)這些服務(wù),因?yàn)槲艺J(rèn)為它們才是更好的技術(shù)棧,可以解決所有的開發(fā)效率問題。但事實(shí)上,沒有任何跡象表明我的做法是對(duì)的。
現(xiàn)在在網(wǎng)上還能找到我在這段時(shí)期所做的一些尷尬的事情,你可以把它們搜出來,然后用它們來取笑我。現(xiàn)在的 Etsy 員工還在拿這些東西來調(diào)侃我。
后來我創(chuàng)辦了自己的公司,用上了 Clojure。雖然,這家公司現(xiàn)在已經(jīng)不在了。但請(qǐng)不要多想,公司倒閉并不是因?yàn)槭褂昧?Clojure。
不過我還是很樂意分享這段經(jīng)歷,畢竟我也是個(gè)體驗(yàn)過函數(shù)式編程樂趣的人。
我并不是一個(gè)容易沉迷于開發(fā)技術(shù)的工程師。我的其他演講很少是關(guān)于工程技術(shù)的。
我還沒老到或者脾氣暴躁到成為那樣的人。但通過總結(jié)馬洛斯需求金字塔理論,我也有了自己的看法。
簡單地說,馬洛斯需求金字塔就是指在滿足更高層次的需求之前,需要先滿足較低層次的需求。如果你連肚子都填不飽,哪里還有心情吟詩作對(duì)?
雖然這個(gè)比喻不一定非常貼切,但在軟件開發(fā)領(lǐng)域,這是事實(shí)。如果你還在忙于討論要使用哪個(gè)數(shù)據(jù)庫,怎么可能有時(shí)間去擔(dān)心整個(gè)產(chǎn)品的藍(lán)圖?
幸運(yùn)的是,在我經(jīng)歷的一些場(chǎng)景中,基本需求都得到了滿足,所以我希望也能讓其他項(xiàng)目進(jìn)入這樣的狀態(tài)。
要達(dá)到這樣的狀態(tài),首先要集中注意力。人類專注細(xì)節(jié)的能力是有限的。
我的朋友 Andrew 總是穿同一牌子的黑 T 恤。他認(rèn)為,如果把花在挑選衣服上的精力囤起來,就可以把它們花在其他更有意義的事情上。
我不知道這樣做算不算缺乏品味,但我覺得是有意義的。
接下來我要談?wù)勎业南敕ā<僭O(shè)我們手上有一些代幣,但數(shù)量有限。
這些代幣代表了我們的創(chuàng)新能力或解決困難挑戰(zhàn)的能力。在一家公司的早期,我們可能有三枚這樣的代幣。
那么你的公司會(huì)怎么做?Etsy,也就是我之前工作過的公司,宣稱要重塑整個(gè)世界的經(jīng)濟(jì)。
我不知道開發(fā)者該如何看待一家科技公司所宣揚(yáng)的使命,我是覺得壓根就不應(yīng)該把它當(dāng)回事。
不過,現(xiàn)在姑且讓我們保留一點(diǎn)點(diǎn)“天真”,看看如果這家公司真的要改變世界,結(jié)果會(huì)怎樣。
重塑整個(gè)世界經(jīng)濟(jì)不是一件小事,可能需要花掉一個(gè)代幣。
離開 Etsy 之后,我加入了另一家公司,這家公司想要“增加互聯(lián)網(wǎng)的 GDP”。
這個(gè)看起來也是件大事,所以可能也需要花掉一個(gè)或兩個(gè)或所有的代幣。
即使你認(rèn)為創(chuàng)新是一種稀缺資源,但對(duì)于已經(jīng)很成熟的數(shù)據(jù)庫或編程范式來說,創(chuàng)新并沒有太大意義。
問題不在于說它們的創(chuàng)新不可行,實(shí)際上,它們是可行的。
但在軟件開發(fā)領(lǐng)域,越是晚出現(xiàn)的東西越是需要更多的關(guān)注。
為了找出其中的原因,我想從哲學(xué)方面入手。對(duì)于一項(xiàng)技術(shù),我知道多少東西?
我不喜歡 Donald Rumsfeld(美國前國防部長),我希望他滾蛋。但他與我們要討論的問題有關(guān),所以我不得不忍受他惡魔般的存在。
在面對(duì)未知事物時(shí),通常有兩種情況。
一種是“已知的未知”(known unknown),也就是那些我們知道自己不了解的東西。還有一種是“未知的未知”(unknown unknown),也就是那些我們不知道自己不知道的東西。
這是一個(gè)“已知未知”的例子。當(dāng)數(shù)據(jù)庫發(fā)生故障時(shí),我們可能不知道具體原因是什么,但我們知道網(wǎng)絡(luò)分區(qū)有可能是造成故障的原因之一。因?yàn)槲覀冎烙羞@個(gè)可能性,所以可以進(jìn)行針對(duì)性的測(cè)試?;蛘呶覀円部梢噪p手合十,希望這個(gè)跟網(wǎng)絡(luò)分區(qū)沒有關(guān)系。不管怎樣,我們都知道有這個(gè)可能性存在。
還有一些是“未知的未知”。這是我最近看到的一個(gè)例子:一個(gè)人花了四個(gè)月時(shí)間試圖弄清楚為什么會(huì)出現(xiàn) GC 停頓,結(jié)果發(fā)現(xiàn)是因?yàn)樗募袑懭肓私y(tǒng)計(jì)信息。顯然,他事先并不知道會(huì)發(fā)生這樣的事情。
軟件中的很多 bug 都是這樣的。我們并不知道系統(tǒng)里存在這些 bug,它們都是“未知的未知”。
現(xiàn)在,我們知道這兩種“未知”在軟件系統(tǒng)中都存在。一個(gè) 10 年的老軟件系統(tǒng)通常在 JIRA 里會(huì)有很多相應(yīng)的 ticket,沒有人會(huì)去修復(fù)這些問題。除了這些,系統(tǒng)里還會(huì)有很多未知的 bug。
但并非所有技術(shù)都是一樣的,新技術(shù)更有可能出現(xiàn)這兩種情況。
我選擇了“無聊的技術(shù)”作為標(biāo)題,并為此后悔了好多天。因?yàn)橛腥苏f:“無聊的就是糟糕的,你為什么還說它是好東西呢?”
但我所說的“無聊”與 CSPAN(美國總統(tǒng)電視系列)的無聊是不一樣的,我所說的“無聊”是指有些東西我們已經(jīng)很了解了。如果你認(rèn)為一項(xiàng)技術(shù)不夠好,那是因?yàn)槟阋呀?jīng)知道其中的原因了。
你可以只使用 Postgres,這樣就萬事大吉了?不,你所選擇的技術(shù)組合也有問題。
假設(shè)你已經(jīng)使用了這個(gè)技術(shù)棧:Python、Memcached、MySQL 和 Apache。
然后你有一個(gè)新的問題需要解決。那么,你認(rèn)為在技術(shù)棧里加入 Ruby 會(huì)有助于你解決問題嗎?
幾乎所有人都會(huì)說:“天哪,這怎么可能!”
我們知道,加入 Ruby 給我們帶來的邊際效益并不會(huì)多過它給我們帶來的復(fù)雜性,因?yàn)?Python 和 Ruby 基本上就是類似的東西。
好吧,那么加入 Redis 呢?我們已經(jīng)有 MySQL 和 Memcached 了,還需要加入 Redis 嗎?
從我的經(jīng)驗(yàn)來看,這就是問題的關(guān)鍵所在。出了問題就打著多語言編程的旗號(hào),就像帶著一班人攻擊巴士底獄一樣,然后說:“你不能阻止我們使用最好的工具來完成這項(xiàng)工作?!?/p>
當(dāng)人們開始屈服于這種本能,就會(huì)說服自己:這是在給開發(fā)者更多的自由。的確,這是一種自由,但其實(shí)是一種狹義的自由。
那么,這背后究竟是怎么回事?讓我們來探究一下。
當(dāng)我們要往項(xiàng)目里添加一些半冗余的技術(shù)時(shí),通常是這想的:“這項(xiàng)新技術(shù)在短期內(nèi)會(huì)讓開發(fā)變得更容易,它給我們帶來的好處超過了采用新技術(shù)所耗費(fèi)的成本”。
我們可以換一種結(jié)構(gòu)化的思維來考慮這個(gè)問題。
假設(shè)你的工作就像我的朋友 Coda 所說的那樣:通過技術(shù)來解決業(yè)務(wù)問題。
因?yàn)槲覀兯诘念I(lǐng)域和計(jì)算機(jī)科學(xué)有一定關(guān)系,所以不妨先假設(shè)自己是計(jì)算機(jī)科學(xué)家,并用二分圖來描述我們的問題。
我們需要把左圖所有的點(diǎn)與右圖的點(diǎn)連接起來,這樣才算把問題解決了。連接一條邊表示做出了一項(xiàng)技術(shù)決策。
每一個(gè)技術(shù)決策都有一定的維護(hù)成本,但同時(shí)也享受著新技術(shù)帶來的好處。
也就是說,每一項(xiàng)技術(shù)決策既會(huì)給我們帶來好處,也需要我們承擔(dān)一定的成本。
我們可以使用之前已經(jīng)選好的技術(shù)。
也可以選擇其他技術(shù)。選擇新技術(shù)需要承擔(dān)相應(yīng)的成本,但如果采用新技術(shù)會(huì)加快開發(fā)速度,那么就是值得的。
我們?cè)噲D最小化這個(gè)成本函數(shù)??偝杀镜扔诳偩S護(hù)成本減去開發(fā)速度的提升和從這些技術(shù)決策中得到的好處。
我們的行為實(shí)際上取決于方程中的哪一項(xiàng)在現(xiàn)實(shí)當(dāng)中占主導(dǎo)地位。
如果技術(shù)維護(hù)成本很高,那么成本就占主導(dǎo)地位。如果技術(shù)極大地改變了開發(fā)方式,那么技術(shù)所帶來的好處就占主導(dǎo)地位。
所以,你可能會(huì)像圖中所示的那樣,使用不同的技術(shù)把所有問題都解決了。
如果說每增加一種技術(shù)的成本很低,那么這樣做就沒什么問題。
這是另外一種策略。我們只選擇了一部分技術(shù)來解決所有問題。
當(dāng)新技術(shù)會(huì)給我們帶來很大負(fù)擔(dān)時(shí)才會(huì)這么做。
在現(xiàn)實(shí)中,新的技術(shù)決策通常伴隨著很大的負(fù)擔(dān)。
長期維護(hù)一項(xiàng)技術(shù)的成本通常會(huì)超過它能夠給我們帶來的便利性。
所以,通常我們會(huì)選擇可以解決所有問題的最小技術(shù)集合。
要把技術(shù)維護(hù)做到很專業(yè)其實(shí)是很難的。在剛開始時(shí)可能很容易,但越到后面就越難。
為什么會(huì)這樣?添加新技術(shù)很容易,但要維護(hù)好并不容易。
比如,我現(xiàn)在就可以用 brew 安裝一個(gè)數(shù)據(jù)庫,然后開始寫代碼。但要在生產(chǎn)環(huán)境中運(yùn)行整個(gè)系統(tǒng)又是另一碼事了。
所以,技術(shù)多元化并不是我們要追求的自由。
如果你將局部決策權(quán)賦予個(gè)別團(tuán)隊(duì),會(huì)對(duì)全局造成傷害。
從某個(gè)角度來看,這確實(shí)是一種自由。你把一團(tuán)鏈條和掛鎖交給了開發(fā)者,然后告訴他們可以自由地做出技術(shù)決策。這些技術(shù)決策會(huì)一直伴隨著它們,直到死神來臨……
更糟糕的是,技術(shù)多元化不僅會(huì)給我們帶來重復(fù)勞動(dòng)和維護(hù)開銷,還消除了使用共享平臺(tái)給我們帶來的積極正面的好處。
以 Etsy 網(wǎng)站的活動(dòng)頁為例。
Twitter 和 Facebook 都有類似的功能。Etsy 在經(jīng)歷了多年的風(fēng)投之后,也想要一個(gè)跟他們類似的功能。
所以,在 2000 年,我開發(fā)了這個(gè)小功能。
要開發(fā)一個(gè)活動(dòng)流功能,可以像下面這樣:從外部獲取事件,把事件寫入數(shù)據(jù)庫。然后另一個(gè)進(jìn)程(比如機(jī)器學(xué)習(xí))對(duì)這些事件進(jìn)行聚合,把結(jié)果寫到 Redis 里,最后前端流量就可以訪問 Redis 里的數(shù)據(jù)了。
在我們剛開始開發(fā)這個(gè)功能時(shí)還沒有 Redis,所以我們選擇了 Memcached。它們的功能很類似:存儲(chǔ)二進(jìn)制對(duì)象,然后通過相似的 API 獲取對(duì)象。但它們?cè)谀承┓矫婧懿灰粯樱瑢?duì)于我們來說,最大的不同是 Redis 支持持久化,而 Memcached 不支持。
也就是說,在使用 Memcached 時(shí),我們需要做一些額外的工作。因?yàn)榭赡墚?dāng)你需要某些數(shù)據(jù)時(shí),Memcached 剛好給你弄丟了。
為此,我們需要多做很多額外的開發(fā)工作。但我們將這些額外工作與新加入一個(gè)數(shù)據(jù)庫可能帶來的運(yùn)營成本相比,還是決定硬著頭皮繼續(xù)使用 Memcached。
就這樣過了幾年,我們也很少想起這件事。
有一天,我不禁想起了這個(gè)功能。然后很吃驚地發(fā)現(xiàn)這個(gè)功能的使用流量比最開始時(shí)增加了 20 倍。
這個(gè)功能的用戶增加了 2000%,而我竟然毫不知情。當(dāng)然,這無疑是我技術(shù)職業(yè)生涯的一個(gè)巨大成就。
之所以會(huì)出現(xiàn)這種情況,是因?yàn)槲覀兪褂昧斯蚕砘A(chǔ)設(shè)施。共享基礎(chǔ)設(shè)施會(huì)有專人照看,他們會(huì)在必要的時(shí)候增加更多的 MySQL 實(shí)例或者其他資源,還有一些人負(fù)責(zé)把新機(jī)器運(yùn)行數(shù)據(jù)中心,把它們加到基礎(chǔ)設(shè)施里。這是一種橫向伸縮。但是,這些人都不知道我們開發(fā)的這個(gè)應(yīng)用程序。
如果我們當(dāng)初使用的是 Redis,那么可以肯定的是,在用戶流量增加 20 倍之后,一定會(huì)在某個(gè)時(shí)刻出現(xiàn)瓶頸。如果是這樣的話,我們就不得不自己去倒騰 Redis 的擴(kuò)容問題。
或者更有可能是讓其他人來做這件事情,因?yàn)槲覀兊膱F(tuán)隊(duì)在一年后解散了。
但我發(fā)現(xiàn),人們其實(shí)并不喜歡給別人擦屁股,所以這件事對(duì)于任何人來說都是很件苦差事。
我想借這個(gè)例子告訴大家,請(qǐng)盡量選擇成熟的技術(shù),而且盡量不要使用過多的技術(shù)。
但這也只是個(gè)建議,請(qǐng)不要把它當(dāng)成是一個(gè)準(zhǔn)則,因?yàn)橛袝r(shí)候往技術(shù)棧里添加新技術(shù)也是有必要的。
問題在于如何選擇新技術(shù)。
簡單地說就是:你要和大家一起討論。
因?yàn)樵诠緦用娌捎眯录夹g(shù)涉及到整個(gè)公司的利益,并不是工程師個(gè)人的事情。
在討論過程中,你可以這么問:“如果不使用新技術(shù),那該怎么解決眼前的這個(gè)問題?”
這個(gè)問題其實(shí)就是要采用新技術(shù)的一個(gè)征兆。“可能是因?yàn)槲覀儧]有使用 Cassandra,要不我們?cè)囋嚳础薄V灰叨噙@一步,后面就可以停止討論了。
假設(shè)你在現(xiàn)實(shí)當(dāng)中遇到了這樣的問題。比如,你在生產(chǎn)環(huán)境中部署了一個(gè)服務(wù),而你認(rèn)為如果不在使用新技術(shù)就無法完成新的功能,那可能是因?yàn)槟阆氲貌粔蚨唷?/p>
你可能需要使用一些特別的方法,但不管怎樣,你完全可以基于簡單的技術(shù)棧獲得更好的效果。
你可以把需要做的事情列出來,你會(huì)發(fā)現(xiàn)情況并沒有想象的那么糟糕。即使它們看起來很糟糕,但肯定不會(huì)比維護(hù)新技術(shù)更糟糕。
當(dāng)然,也會(huì)出現(xiàn)另一種情況:當(dāng)你把所有東西都列出來后,你會(huì)發(fā)現(xiàn)采用新技術(shù)可能更值得。
如果你決定采用新技術(shù),那么就要想辦法把風(fēng)險(xiǎn)降到最低。不要試圖一次性重寫整個(gè)應(yīng)用程序,而是要循序漸進(jìn),每次修改一點(diǎn)點(diǎn),逐步建立信心。
如果你往技術(shù)棧里添加了與之前類似的技術(shù),那么就要想辦法把舊技術(shù)替換掉,而不是同時(shí)維護(hù)兩種技術(shù)。這是一個(gè)長期的計(jì)劃。如果發(fā)現(xiàn)新技術(shù)不合適,還要有回退的能力。
總 結(jié)
選擇人們所熟知的技術(shù)。
所選擇的技術(shù)應(yīng)該能夠讓你把注意力放在值得的東西上。
從整體考慮問題,你所選擇的技術(shù)應(yīng)該能夠覆蓋到整個(gè)問題領(lǐng)域,并解決所有問題。
掌握你所選擇的技術(shù),這一點(diǎn)很關(guān)鍵。
我發(fā)現(xiàn)幾乎所有的軟件系統(tǒng)都遵循這個(gè)定律。在剛開始時(shí)你會(huì)覺得它們很爛,因?yàn)槟銜?huì)碰到很多問題。
如果你是一個(gè)新手,可以試著在實(shí)踐中驗(yàn)證這條定律。你可以試著把新開發(fā)的功能部署到生產(chǎn)環(huán)境,然后你就會(huì)想:下一個(gè)新功能我想嘗試新的技術(shù)。
但使用新技術(shù)并不一定會(huì)更好,因?yàn)槟愀静恢浪鼈冞€會(huì)出什么問題。
可能是因?yàn)槟闾^了曲線的”Mastery“部分。盡管在這個(gè)階段仍然存在問題,但你會(huì)覺得這些問題是可控的。
這個(gè)定律有一個(gè)可怕的悖論:或許你要使用你最討厭的技術(shù),因?yàn)槟阍绞怯憛捤?,說明你對(duì)它越了解。
在使用新技術(shù)時(shí),你需要遵循一定的流程,比如先和其他人一起討論。
你應(yīng)該試著順著馬洛斯需求金字塔網(wǎng)上爬,從全局看問題,而不是每天想著是使用這個(gè)數(shù)據(jù)庫還是那個(gè)數(shù)據(jù)庫。如果你每天的工作就是干這個(gè),那一定是哪里出了問題。
做有意義的工作會(huì)讓你幸福感滿滿。
好了,現(xiàn)在可以把我從坑里挖出來了嗎?