Categories
程式開發

谷歌首席軟件工程師:如何設計大型應用程序


本文最初發佈於Medium博客,經原作者授權由InfoQ中文站翻譯並分享。

這是我在JSConf Hawaiʻi大會上的演講筆錄。

嘿,大家好!我叫Malte,是谷歌的首席軟件工程師,今天我想談談如何設計大型應用程序。這是我兩年前在JSConf Australia的一次演講的續集。就像上次一樣,我的演講將立足於軟件工程師的職業發展。我想,在座的很多人會稱自己為高級工程師;或者,如果你還沒有做到,你渴望成為其中的一員。

我將描述什麼是高級工程師,如果有人到我這裡來說,“嘿,Malte,你熟悉這個領域的這個項目嗎?”,我會說,“是的,我確實知道怎麼做。我不需要別人的幫助。”

在上一次演講中,我探討瞭如何在軟件工程中超越這個級別。不再只是關於你自己,而是你的技藝開始影響其他工程師。你會說,“我可以預見到別人怎麼做,然後設計相應地API。”

在這次演講中,我還想再進一步。我想達到這樣一個境界,我可以說:“我可以為大量的工程師設計軟件,這樣可以增加他們開發出優秀軟件的概率。”

谷歌首席軟件工程師:如何設計大型應用程序 1

這句話有三個關鍵詞。

  • 首先是“大量的工程師”。如果你在一家初創公司或三人公司工作,那麼這個演講可能會顯得多餘和無聊。但是我想你們中的許多人可能在保險公司、銀行、機構、大型科技公司等工作——這些公司足夠大,你有一群人,多個團隊,你需要協調工作。
  • 接下來我要講的是“概率”。這裡沒有確定性。你只能試著把事情安排好,這樣它們才有可能會奏效。但沒人能保證。沒有什麼靈丹妙藥。
  • 第三個關鍵詞是“優秀”。這不是關於產品管理的演講。這也不是一個能幫你寫出正確程序的演講,但它能幫你寫好程序。我的意思是它具有可維護性、高性能、低Bug密度、按時交付等等。

我還想澄清一件事:我會經常提到框架和軟件基礎設施這兩個詞。我指是幫助我們構建更好的軟件的軟件。在這次演講中,我希望你們所有人,都能把你們自己當成是你們公司中一個負責定義人們如何編寫軟件以及構建用來編寫軟件的基礎設施的人。當我說你構建一個框架時,我並不是說你一定要做出自己的React或Angular。相反,我認為,當你有一系列團隊時,你想要標準化他們構建應用程序的方法。所以,你提供了一個嚴格的框架。

再說一下,把你自己想像成負責團隊軟件基礎設施的人。這是一個關於如何讓你在工作中取得成功的演講。我將分三章來講這個問題。

  1. 第一章我稱之為了解不確定性的程度。
  2. ……然後我們將學習如何解決所有已知的問題。
  3. 最後,我們將學習如何部署變更。

了解不確定性的程度

我們能在多大程度上確定未來需要解決的問題的類型?這個問題絕對是軟件工程的關鍵,我要講的這種技術可以幫助我們了解我們對事物的了解程度。

谷歌首席軟件工程師:如何設計大型應用程序 2

海森堡不確定性原理的發現者維爾納·海森堡的照片(圖片來源

我們要做的是試著回答下面這一系列的問題:

  • 用戶在現有的基礎設施中遇到了什麼問題?他們是如何解決的?
  • 從廣義上講,人們將使用你的工作成果構建什麼樣的應用程序?
  • 影響行業的趨勢是什麼?這些趨勢對人們將來可能編寫的軟件有什麼影響?

所有這些聽起來可能有點像預言,但我認為,借助一點經驗,有很多場景下,你都可以做出很好的回答。這個活動的關鍵是觀察回答問題的難易程度。

如果真的很簡單,你把它寫下來,一切都很清楚,那麼你的不確定性就很低。然而,如果你真的不知道如何回答,那麼你就有很大的不確定性。

比知道你需要做什麼更重要的是知道你不需要做什麼。那些是非目標。

非目標是什麼?如果你在一家軟件公司工作,說“我不需要設計衝浪板”並不是一個非目標。那不是非目標,那隻不過是無稽之談。當然,你不想這麼做。非目標實際上是非常合理的,甚至可能是你想做但你知道你不需要做的事情(比如“我需要支持低端設備”)。如果你能輕鬆地定義你的非目標,你就會知道你的不確定性還要低。

那麼,為什麼不確定性低很重要呢?當不確定性很高的時候,你將不得不做出權衡並保持靈活性。你可能會想“我可能需要支持這個特殊情況,所以我最好確保它是可能的”。你可能需要做出權衡來實現這種特殊情況。權衡的問題是,它們使事情變得複雜,不那麼理想,因為你必須在相互衝突的關注點之間找到平衡。

當高度的不確定性導致你需要做出不必要的權衡時,事情就會變得讓人不愉快。

不必要的權衡是萬惡之源。

讓我們來看一個例子:假設你正在為公司內部的移動應用程序構建基礎設施,可能銷售人員都會有內存較小的舊手機。因此,你設計的東西要可以在低內存設備上工作。但是,你公司會給每個人都買漂亮的新手機。所有支持低端設備的工作都成了不必要的,更糟糕​​的是,這可能會使軟件在未來變得更加複雜。有趣的是,這是對Donald Knuth的“過早優化是萬惡之源”的泛化。過早優化是不必要的折衷的一種特殊情況。每次你設計軟件去做一些它並不真正需要支持的事情,就可能是一個不必要的取捨權衡。

我是在一個Web開發會議上做演講,所以我希望有一個關於Web開發的部分。作為一個社區,我們已經構建Web框架好幾年了。現在有20多年了。我們確實知道如何很好地構建Web框架。人們想用Web框架做什麼的不確定性真的很低。

將此與下面這個場景進行下比較:假設現在是2015年,你的經理來了,說“我希望你為深度學習構建基礎設施”。你可能會說,我從來沒做過這種事。我們公司以前也沒有人這樣做過。關於這個話題,一共有三篇博客文章。不確定性真的很高。到2020年,你將無法構建出像Web框架一樣優秀的基礎設施。

處理不確定性

谷歌首席軟件工程師:如何設計大型應用程序 3

外科醫生戴醫用手套的照片,來自Getty

問題是,如果不確定性很高,我們該怎麼做?我們要擬定抽象的程度。

讓我解釋一下:抽象非常棒。你希望盡可能多地抽象所有內容,因為抽象可以使所有內容真正具有表達性、更準確、可重用,而且通常更令人敬畏。然而,如果抽像不允許我們做我們想做的事情,那麼事情很快就會落空。我們現在必須想辦法解決它,打敗它們然後做我們想做的事。一切都突然變得痛苦和可怕。

如果不確定性高,則降低抽象程度。

谷歌首席軟件工程師:如何設計大型應用程序 4

這個圖表非常不科學地顯示了不確定性和抽象性之間的關係。

這就是為什麼我們需要很好地理解問題空間:它允許我們根據不確定性程度調整抽象。如果不確定性很高,那麼我們就降低抽象程度。

谷歌首席軟件工程師:如何設計大型應用程序 5

抽象藝術的照片(圖片來源)和女人的照片(圖片來源,由Sheena876提供,遵循CC許可

解決所有已知的問題

每個軟件項目都有一定程度的不確定性。因此,有一套有效的技術是很有用的——不管我們有多麼不確定:我們將解決軟件工程的所有已知問題。

迭代速度

第一種技術是優化迭代速度。我的意思是:你寫了一些代碼,然後在編輯器中點擊保存,從那一刻到你發現修改是否存在問題需要多長時間?你可能有超級棒的熱代碼重載,所有東西都像魔術一樣立即更新,或者你可能需要手動關閉Java應用程序服務器,重新編譯,並重新啟動服務器,這大約需要20分鐘。我想我們很多人可能處於這兩個極端之間。

實際上,緩慢的迭代周期就需要我們在軟件設計時做出權衡。如果你花了很長時間才發現你犯了一個錯誤,那麼作為一名軟件設計師,最專業的做法就是說,“我必須設計這個API,這樣才不會有人出錯”。另一方面,如果一切都非常快,人們可以迭代地解決問題,因為失敗的成本很低,那麼作為API的設計者,你可以說,也許人們可以在這裡做更多的探索。

可調試性

下一個是可調試性。作為框架作者,我們實際上對系統調試的難度有很大的影響。也許你設計了這個令人驚嘆而復雜的黑盒狀態機,沒人能弄明白。也許你的堆棧跟踪超長且難於理解。或者,你可以設置一個合理的日誌記錄和跟踪系統,這樣人們就可以理解正在發生的事情。他們犯了錯誤,他們調試它,他們修正它。這沒有問題。

可測試性

與可調試性類似,你可以控制系統的可測試性。也許你的框架讓實例化處於給定狀態的東西變得非常困難,以至於無法進行測試?或者你讓它變得很簡單。人們將編寫更多的測試,擁有更強的信心,並成為你的框架的更愉快的客戶。

同理心

谷歌首席軟件工程師:如何設計大型應用程序 6

兩隻貓的照片,來自Getty。一個說“我不是什麼都知道”。另一個說“沒關係”。

對於解決軟件工程中所有已知問題的最後一種技術,我們將稍微遠離純軟件方面的東西。我們要談的是同理心。

就像我在上次演講中提到的:作為一個軟件工程師,與其他軟件工程師產生共鳴是一種簡單模式下的共鳴。相比一個我們對其背景了解甚少的人,對於軟件工程師,我們對他們對某件事情的感覺的直覺,更有可能是正確的。今天我想談談同理心的一個非常特殊的方面:你作為框架的設計者可以用它來構建完美的應用程序。你什麼都知道。你可以把每件事都做好。但是其他使用你的框架的人對它的了解要少一些。

考慮一下,不完全了解你的框架意味著什麼。

所以,考慮一下不完全了解你的框架意味著什麼。以及如何在這些不完全了解它的用戶面前使框架變得健壯。

變更部署

既然我們已經學會了解決所有已知的軟件工程問題,那麼讓我們進入最後一章,變更部署。

如果沒有用戶,那麼它就沒有影響。

這部分非常非常關鍵。軟件基礎設施,不管它有多好,如果沒有用戶,那麼它就沒有影響。然而,在這個領域,人們在沒有真正的用戶的情況下建造象牙塔是非常普遍的。他們構建一些讓他們興奮的東西,構建過程可能真的很有趣。然後他們過來說,“嘿,我有個東西”,然後你說,“但是那個東西不能滿足我的需要”,每個人都很傷心,然後他們繼續建造下一個像牙塔。

如果你想專業化地構建軟件基礎設施,那麼這顯然不是正確的方法。讓你的作品被採用就是一切。這是你工作的重要組成部分。因此,對於如何正確地做這件事,我的第一個建議實際上完全是關於市場營銷領域。

軟件工程師也是人。他們想要做一些他們認為很酷並且在Twitter上聽說過的東西(比如無服務器、機器學習、虛擬DOM、今年大肆宣傳的東西)。我認為,在你的框架中加入一些閃光點是很好的。只要它不是世界上最糟糕最不必要的設計權衡。讓每個人都更快樂一點,不會造成什麼傷害。這就是這次演講的營銷部分。讓我們進入一個更嚴肅的話題。

增量採用

如果你可以增量地採用軟件,則這個過程會更容易。這意味著,你不用這樣,“嘿,試試這個新框架,我們用兩年的時間重寫了所有的東西,希望它不錯”,而是一步一步地遷移較小的部分——因此,在用兩年的時間完全重寫之前,你就看到了積極的影響。我所看到的是,即使項目一開始的目標是完全重寫,也會經常提前交付至少幾個部分,因為雙線工作(維護遺留系統和構建新系統)的壓力會增加,而管理層又希望看到結果。

然而,對於框架設計者來說,增量採用之路並不好走。下面這些技術已經被證明在這種情況下是有效的。

遺留代碼調製

第一種方法非常簡單,分為兩部分。

  1. 首先是將新的框架代碼組合到遺留代碼中:假設你還沒有開始重寫應用程序Shell。該應用程序仍然是建立在舊的架構上。但是團隊過來說,“我要基於新框架構建新特性,並把它放到舊的代碼庫中。”也就是將新代碼組合到遺留代碼中。
  2. 另一方面是將遺留代碼組合到新的代碼庫中。假設你有一個非常好的自定義組件,一個jQuery日期選擇器,人們想要保留它,對吧?在你設計的框架中,這種jQuery的東西,可能違反了所有你曾經做過的假設,但在你的新代碼庫中仍然要可以使用。也就是將遺留代碼組合到新代碼中。

這兩種策略都會給你的系統帶來取捨壓力,因為突然之間所有這些假設都不再有效了。但是這種權衡取捨可能是值得的,因為它允許人們增量地採用你的軟件。

暫時的不完美

第二種技術我稱之為暫時的不完美。作為一個框架作者,你可能對人們如何使用你的框架構建軟件有一個非常理想化的看法。但是在增量採用的過程中,它可能會崩潰,因為並不是所有的東西都那麼理想,而是新舊代碼交織在一起。

我的建議是這樣的:使用像ESLint這樣的工具編寫一個文本檢查規則,它可以識別舊的編碼方法。然後,當代碼庫中有代碼沒有以新的方式編寫時,就會得到一個錯誤消息。最好錯誤消息還能告訴你一些有用的信息,比如“這種寫法不再適用了。這裡有一些文檔可以告訴你如何正確編寫。”

第二步是使用許可列表來允許所有已有的違規行為。這樣一來,你就沒有代碼需要修復了,但是所有新代碼都必須遵循新的編寫方式。這非常有效,因為組織中可能有很多工程師,他們沒有閱讀電子郵件,不知道他們需要做一些不同的事情。現在他們看到了一條錯誤消息,就會轉身去修復它。

技術債務分類賬

谷歌首席軟件工程師:如何設計大型應用程序 7

賬簿照片,來自Getty

還有一個更有趣的觀點。你檢入存儲庫的這個許可列表,實際上是一個技術債務分類賬。技術債務可能就是這樣一個抽象的概念,你知道你有,它可能在某個地方,但很難確定它在哪裡。分類賬可以讓你知道文件X.js第15行有技術債務。

知道技術債務在哪裡,是還清它的第一步。你可以讓一個團隊來解決它,你所要做的就是縮短許可列表。我認為這是一種讓技術債務具體化的有力方式,就像你的銀行賬戶債務一樣。

自動遷移

下一項技術是支持自動遷移。它不會幫你做所有的事情,但它是相當強大的。像Facebook的codemod這樣的工具可以幫助你實現這一過程。你聲明如何從A到B,然後電腦完成剩下的工作。簡直是太好了。

正如本文中的許多內容一樣,針對自動遷移進行設計會給新API帶來權衡取捨的壓力,因為現在,在理想情況下,新API只需要現有代碼庫中已經可用的信息。但這可能是值得的,因為不需要人手工來做這些工作,這將使人們比手工做每件事更快樂。

零號客戶

我們再來談談人。我希望每個人都能從這次演講中學到的一點是,當你作為軟件基礎設施的負責人時,你總是需要一個零號客戶。零號客戶是像牙塔的解藥。他們是你的第一個客戶,你將幫助他們構建一個真正的產品。

零號客戶是像牙塔的解藥。

你需要做的是和他們一起編寫軟件——成為他們團隊的一部分。這一切都發生在你宣布你的框架可交付之前,因為在這一步中,你可以實際驗證你的假設。你可以找到一些極端的情況,你可以在迭代你的框架,直到它真的很好。第一個客戶所做的不應該是遷移。它應該是從零開始構建一個東西。因為遷移總是有點痛苦、漫長和曲折,你真的不想遷移到還沒有經過證明有效的東西上。

作為基礎設施工程師,一個比較諷刺的地方是,你很少使用自己的基礎設施來構建任何東西。這使得編寫基礎設施入門文檔變得非常困難。如果你有一個零號客戶,你和他們一起工作,這是最好的機會,你可以為他們編寫入門文檔。不要錯過機會!

實際上,在大型組織中,找到零號客戶是很有挑戰性的。你可能找不到一個團隊願意使用不穩定且可能相當糟糕的框架。我發現了一些很好的論據來說服一個團隊成為你的零號客戶:

  • 通常會有喜歡走在前沿的團隊。你可以做的一件事是說,“嘿,你最終將不得不遷移到這個,因為它是未來每個人都必須使用的技術棧。你為什麼不想現在就開始做一件新的事情,跳過遷移呢?”這對一個團隊來說是很有吸引力的。你可以爭辯說,因為你要和團隊一起工作,他們可以獲得一個為他們量身定做的框架,而不用像其他人那樣要湊合著用現有的框架。
  • 如果這招不奏效,你就得採取更嚴厲的策略。在這種情況下,組織必須足夠成熟,同意強制執行。有人將不得不決定哪個團隊將成為零號客戶,他們將不得不接受它。因為另一種選擇是建立一個像牙塔。這對所有人都不好,不僅僅是這一個團隊。

零號遷移

最後,我們會第一次將現有的代碼庫遷移到我們的新的基礎設施。再一次,你要自己進行遷移。這是因為你的框架實際上可能還不是一個很好的遷移目標。假設你的框架在B點,某個項目在A點。從A點到B點可能超級困難。但是,作為框架的作者,你可能會看到對B點進行小幅修改的機會,從而更容易實現目標。因為你是一個懶惰的人,你將使遷移變得非常容易,這將幫助每一個在你之後遷移的人,讓他們有一段更美好的時光。

小結

這是我想談的三件事。讓我快速地總結一下:

  1. 我們需要做的第一件事是了解不確定性的程度,然後分別調整抽象的程度。
  2. 即使不確定性很高,我們也希望解決所有已知的問題:增加迭代速度,使軟件可調試、可測試,對於人們可能不知道的事情,我們要有同理心。
  3. 最後,如果你不關注團隊採用,那麼這些就都無關緊要了。讓你的軟件變得招人喜歡,專注於增量採用,做一個技術債務的分類賬,找到零號客戶,完成零號遷移。

今天就談這些。非常感謝!

原文鏈接:

Designing Even Larger Applications