你知道Facebook工程師是如何高效工作的嗎?
編者按:Facebook 的工程師有哪些高效工作的經驗呢?軟體工程師訪談了多位 Facebook 的高產工程師,總結了他們的共同經驗以及晉級之路,供各位參考。
成為高效開發者這件事你可以通過經驗、書本、或者試驗和錯誤來學習。但成為高效開發者的最有效方式之一是直接向高效開發者學習。我訪談了 Facebook 的幾位最高產的工程師,想找到這些開發者實現最高生產力的基礎結構是什麼。
第一級:減少不必要的干擾
這一點似乎很明顯,但是正是這些累積起來的小事情最影響我們的生產力。
避免開會
我儘量少開會。例會我一般都不參加。這條未必適合每一個人,因為經理喜歡安排例會,你也不想把經理給惹毛了,但我建議給他看看開會的成本:(10 工程師 x30 分鐘)/ 周 +10 分鐘任務切換開支=浪費工程師半天時間 / 周,這可不是小數。我總是努力爭取用有議題的討論或者站立會議來代替例會。
—匿名
許多工程師都強調必要的時候會議是有用的。但是必須對開會保持批評性的眼光,不開不必要的會。
準備好做小任務
在生成或者修訂的時候我會迅速清理郵箱保持收件箱為 0— Michael Novati
有時候在你完成手頭任務之後到開會之前會有 5 到 15 分鐘的間隔,或者你進行的試執行可能需要花費 1、5 或 15 分鐘的時間。對此通常的反應是 “這種時間裡面做不了什麼大事的。” 這種說法當然沒錯,許多工可能都要花 30 分鐘到 1 小時的時間才能集中注意力,但並不是所有任務都這樣。許多工程師提到自己會做一份小任務清單,這樣在每天稍微閒暇的時間裡就能把那些事情處理掉。比如處理郵件、diff(參見附註)評審,回覆內部帖子,或甚至可以進行小規模的 diff/ 重構,從而提高一天的工作效率。
戴上噪音消除耳機
如果你在成立時間不久的軟體公司呆過,你就會注意到那裡的辦公區域設定是如何的空曠。對於工程師來說這是一把雙刃劍。一方面你跟團隊的距離可以更加靠近。團隊成員間的協作和友情可以一直保持很高水平,問問題很方便,跟同事關係也可以很融洽。不好的是你寶貴的專注度被環境噪音干擾了。當你正在思考棘手問題的時候,聲音很大的討論會贏影響到你的生產力。這時候噪音消除耳機就可以派上用場了。這種耳機的技術已經非常先進了,遠不止是在你的耳邊豎起一道屏障,調大耳機音量。真正的噪音消除耳機能夠排除環境噪音並且抑制周邊低沉的交談聲。跟 Michael Novati 談過以後我也買了一副他推薦的耳機,說實話要沒了它我都沒法幹活了。很顯然,戴上這樣一副耳機進行對比之後,你就會知道辦公室的環境噪音有多大。
在別人沒法干擾你的時候工作
(關於如何應對干擾)2 年前搬到紐約去可能對我的幫助很大
—Adam Ernst
我通常把更困難更復雜的任務留到星期三(那時候我可以在家工作不受干擾)
—Bob Baldwin
實際上 “正常” 工作時間內我幹不了什麼事情,只有靠加班時間才能完成事情
—匿名
我通常在早上 6 點到 9 點間能夠乾的事情比一天其他時間能幹的都要多,長時間的不受打斷至關重要
—匿名
我得承認,跟這幫工程師聊的最大發現之一是他們當中許多都工作很長的時間。為什麼他們要幹那麼久呢?原因挺有趣的。因為只有在早上、深夜、週末這些時間才沒人打擾他們。通過尋找一天當中受打斷最小的時間,這些人得以推進自己的重大任務,把程式碼給弄出來。
減少煩人的郵件 / 通知
我會關閉所有非緊急的郵件提醒。只接受需要行動的郵件,並且強迫自己按照合理的頻率來檢查任務 / 郵件頁,把其他東西當成垃圾郵件實際上對我是有幫助的,我錯過的東西反而更少了。
— Ari Chivukula
每次收到新郵件時都要停下來去看看的話,你一整天都會被各種干擾打斷了。通過減少和過濾通知到只保留必要的那些,你就能在不受干擾的情況下工作更久。
不要失去狀態
我被打斷(或者必須去洗澡)的時候會在腦中 “儲存狀態”,就像在 Gameboy 模擬器中儲存狀態一樣,這樣回頭繼續的時候我就可以儘快恢復了。
— Michael Novati
永遠要在一項相當簡單或機械的任務中結束一天。這讓你第二天很容易就可以撿回來恢復工作狀態,而不是又要從頭開始。
—Adam Ernst
對於我來說,當我在認真思考問題時、被打斷時、忘記自己在想什麼時就會在程式設計方面會失去狀態,也就意味著我需要重新把整個思考過程再過一遍。
當你在工作狀態時遇到干擾最好的辦法之一是推遲干擾。如果你在全神貫注的時候被幹擾,那就告訴那個人你稍後會處理,速記一下此事,然後繼續工作直到你到達合理的停止點。一旦到達停止點,馬上處理完堆積的那些事情。
如果幹擾無法推遲,也有很多辦法可以 “保持狀態”。比如寫下自己當前的思考過程,寫下失敗測試,或者簡化正在思考的問題。
第二級:寫出 “更好” 的 Diff
好程式碼意味著很多東西。好程式碼應該是功能性的,容易評審的,經得起時間考驗的,等等。
寫更小的 Diff
許多小的 diff 就像是在解決工程問題時 “展示你的工作”
—匿名
我訪談的每一位工程師都非常強調把程式碼變更拆分為邏輯模組,這樣可以讓其他人更容易理解,更快接受。通過減少 diff 帶來的認知負荷,評審者對於接受變更會更有信心。此外,通過減少 diff 的規模,變更對於評審者來說也沒那麼令人望而生畏,這樣評審效率也會更高。
可以告訴你們的是,這篇文章問到的工程師都是過去 6 個月 Facebook 裡面提交 diff 最多的人。可能會有兩種風格的工程師群體,一群是提交很多小的 diff 的,一群是提交數量較少但規模較大的 diff。
堆疊 diff 以及多工處理
大多數工程師提到自己會將 diff 變更相互堆疊起來,逐步建立 diff 間的邏輯依賴:
有時候當我做了一個規模很大的 diff 時,我會回過頭把它拆分成一系列邏輯步驟,這樣我就能慢慢變更一些東西,然後程式碼的總體質量也會逐步得到改善
—匿名
我從來不堆疊 diff,相反,我會並行做幾樣獨立的事情,然後在等待評審的時候交替處理。把一項大的變更分解成獨立部分這種做法也很有效,比方說增加介面,增加端結點,把這些事情安插進待辦事宜裡面……這樣可以把事情堆起來交替做,不用在 diff 之間產生嚴重依賴。對程式碼進行結構化,這樣就不必堆疊或者弄子分支出來,評審、交付、還原都容易一些。
—Michael Novati
如果我覺得自己在把多個 diff 放進一個 diff 的話,我會拿出來放到堆疊上形成相互依賴關係。
—匿名
要做小的,至少是容易評審的 diff。這不僅是為了更容易更快地稽核,也能讓我寫出更好的程式碼,因為我認為如果每次處理的東西邏輯分明的話,浪費的除錯時間就非常少了。此外對堆疊 diff 的經驗可以可以讓小的 diff 可管理。
—Ari Chivukula
我大量使用堆疊 diff。這麼做除了讓我可以在等待程式碼評審時有事可做以外,把程式碼分解更更小的 diff 往往能讓我從巨集觀上考慮自己在做什麼,甚至還能簡化架構。
—匿名
無論工程師是使用堆疊 diff 還是進行 diff 的多工處理,其生產力似乎都相當出色,這說明這兩種辦法對提高效率都很有效。
單元測試
測試規模要儘可能小,這樣我會感覺舒服一點。
—Michael Novati
大家對經過單元測試的程式碼更容易接受些。
—匿名
單元測試這個東西在一些技術公司會引起爭議,大多數團隊和公司對於工程師要不要寫測試都有自己的指導意見。不過有一件事可以肯定,如果你所在的公司別人可以修改你寫的程式碼的話,確保某人不會搞砸你的程式碼的最好辦法是在測試中強制功能要求。
溝通
對於比較棘手的 diff,我會適當增加幾個評審人,把 diff 共享到合適的群裡面。不管有沒有動作,我每天都會 ping 一個 diff 出來。如果好幾天都沒有動作,我會問評審的對 diff 有什麼驚人的發現,然後對結構做出改變。最後我會盡可能跟對方多溝通。我總是以 “[產品 / 標籤]” 作為標題開頭—這樣大家就能知道 diff 是做什麼的,如果我想盡快得到接受,我會寫上 “[product-ASAP]” 或者 “[product-PUSHBLOCKER]” 之類的東西。
— Michael Novati
這一點似乎顯而易見,但是就 diff 評審進行溝通的方式有很多種。一般的經驗法則是按 diff 來進行。如果稽核你的 diff 的人平時不怎麼跟你打交道的話,你可能要在描述和標題上面增加一些平時跟團隊成員溝通時不需要的上下文資訊。你還可以在需要別人重點審查的地方做批註,如果相關邏輯比較複雜的話。
在設計會議上就提出期望還可以簡化寫程式碼的過程,開發出好的 API 和架構等。
不要害怕提醒評審者注意你的 diff。如果他們不想稽核你的 diff,他們可以退出或者建議其他人來做。如果讓 diff 一直留著待審查佇列,那無異於給將來埋下衝突。
第三級:具備團隊精神
編碼是一項團隊運動,就像所有的團隊運動一樣,個人能實現的事情總是有限的。
評審他人的程式碼
快速掃描,通讀,打補丁,測試,評論
—匿名
多進行評審以便提高你的程式碼產量,這句話聽起來似乎有點矛盾。我們可以把 “多做評審” 解讀為 “清理別人的佇列”。當你換個角度看問題時,情況就清楚多了,如果你清理了其他工程師的佇列,那別的工程師也就更有可能幫你清理掉你的佇列,替你解鎖。
與評審者 / 團隊成員建立信任
我有一個核心工程師小組,跟裡面的人維繫著良好的信任關係,我們會一起努力迅速評審對方的程式碼。
—匿名
這一點跟上一點有點類似。如果你有一組核心的工程師,相互能夠信任對方能寫出好程式碼的話,你就可以相對安全地做出變更,因為哪怕某位工程師寫錯了東西,大家也會在出現問題時一起努力改好它。
對自己在做什麼要透明
進行新專案(greenfield development)開發時應該用 RFC(請求評論)diff。相對於在後面才改變方向,預先獲取 /header/ 提議 API 反饋所消耗的時間是值得的。
— Adam Ernst
如果大家不知道你在做什麼,因為評審 diff 時需要的資訊量不足會導致他們困惑。通過儘早把評審者 / 團隊成員納入圈子裡面,他們就能在一大塊工作完成前提供反饋,前進道路的路障也就被解除了。
提供好的反饋
專注於提供高質量的反饋而不是挑刺。如果你發現 bug 了就指出來,但是要相信工程師已經測試過(並且相信他們會改正任何發現的 bug)
—Bob Baldwin
先略讀了解概況,如果有什麼地方不清楚就批註一下或者提出改進建議。如果總體感覺良好,再細讀看看是否存在最佳實踐問題或者小細節問題,沒有的話留幾條註釋接受變更就行了。
— Ari Chivukula
“評審 diff 的策略是什麼” 這個問題的常見迴應是首先從高層視角理解待評審的 diff。在理解了 diff 的基本結構之後,再深入瞭解程式碼風格並進行邏輯檢查。
請求變更
要頻繁使用請求變更—最糟糕的事情莫過於他們重新請求評審(當然要鼓勵作者這麼做,如果對方認為你的請求變更是錯誤的話)
— Adam Ernst
如果我可以看出自己的問題,那就請求單元測試或者重構,這樣出問題的可能性會低一點。如果東西太大太複雜而且顯然沒人把心思放在這上面,那就請求他們結束評審,或者至少給出將來更好做法的建議。
—匿名
在一些非標準事情上對 diff 提出請求變更可能會有點令人尷尬。但是從長遠看,鼓勵採取更好的編碼做法和校驗是值得的,工程師以後會從錯誤中吸取教訓並作出改進中得到回報。
不知道要承認
如果我對程式碼庫的一部分不太清楚我會直接說然後跳過。
— Michael Novati
不懂裝懂是裝不了多久的,如果要評審的東西你不懂就坦白告訴別人,然後讓對方去找別的更懂的工程師評審。
第四級:組織與推進
待辦事宜
我會把日程表裡面的個人事務先放到一邊,設定提醒稍後再辦。如果日程安排裡面不設提醒我會忘了。
— Michael Novati
大多數情況下,我問到的工程師每個人都會使用不同的任務跟蹤工具。同時要跟蹤管理的來源還包括紙、任務、郵件、日程、清單、高階目標等。不過在確定接下來該幹什麼,在對任務、郵件的組織和分類方面,許多工程師都有一個 “層次化” 機制。
快速失敗與迭代
我會盡量快速調整程式碼,哪怕自己不能確定那是(獲得註釋的)最優方案。對於想法的嘗試,我寧願快速失敗而不願先 100% 考慮清楚
—Ari Chivukula
程式碼嚇不倒我,我可以很容易進入狀態 ,兵來將擋水來土掩
— Michael Novati
許多工程師(包括我自己在內)可能會隱瞞的一點是害怕失敗。馬上就要做出完美產品的想法是可怕的,會導致我們考慮太多的問題。要養成在不知道結果會怎樣的情況下馬上寫程式碼的習慣,這樣我們就可以更快地迭代,更快看到結果。
工作 / 生活平衡
有一位比你還要努力的<重要他者>是有幫助的,不然的話你就得自己扛了。我的做法是張弛有度,有時候我會衝刺一段時間然後放鬆一下。大概是高強度 2 個月然後放鬆 1 個月這樣子。在我看來,這要比 3 個月保持相同節奏更高效(不過這一點並不適合每個人),因為工作不是線性的。
—匿名
“洗澡的時候考慮問題” 這個比喻還是有點道理的
— Adam Ernst
這些工程師的確幹活非常努力。為了完成大量的程式碼他們花費了很多的時間。儘管我沒有具體詢問工作 / 生活平衡的問題,但是他們當中很多人工作、生活是分得很清楚的,哪怕是 “全力以赴” 的工程師,這點挺讓我驚訝的。看起來這些人不僅可以高效工作,而且工作 / 生活平衡也能做得很好—如果願意的話。
零星想法
你希望別人怎樣待你,你就該怎樣去對待別人。你得成為別人希望共事的那種工程師,而這大部分是通過反饋來了解的。要早點問,經常問,被問到的人會感覺自己受重視。
— Ari Chivukula
經過這些訪談後,我感覺自己的開發程序正在一點一點地發生演變(同事要很努力才能引起我的注意,因為我現在戴上去噪音耳機了)。拆分 diff,請求評審,待辦事宜清單,這些東西我之前也做,但現在我知道如何更有效地利用這些工具了。說實話,採取了上面的做法之後,我的效率提高了不少。
附註:
1、Diff:Facebook 工程師所謂的 diff 是指用開源工具 phabricator 建立的程式碼修訂(differentialrevision)。這些修訂是放進 phabricator 供其他工程師評審的程式碼改動。工程師可以評論、請求變更或者接受程式碼變更,之後再轉給產品影響實際使用者。Facebook 編寫的每一條程式碼都要經過這一驗證步驟,確保工程師之間的不斷協作以及相互反饋。
2、堆疊 Diffs:是指 diff 的堆疊,上層 diff 邏輯依賴於下層 diff,其功能是基於所有下層 diff 的功能基礎之上開發的。這使得工程師可以在方便評審的簡單小規模的變更基礎上開發功能,循序漸進實現更大的目標。