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