Software Engineering - The Soft Parts

本文翻譯自 Addy Osmani(@addyosmani) 部落格的 「Software Engineering - The Soft Parts」 一文,翻譯已取得作者授權。 分享或轉貼本文章請註明此文章連結以及原作者的文章連結

teamwork

今天,我將分享在 Google Chrome 第一個十年所學到的一些軟體工程師的「軟技能」,我是一位 Senior Staff Engineering Manager。在我的十週年,我想要反思一些伴隨在我身邊的課題。我希望這些經驗對妳的職涯有幫助。

成為一個好的工程師就收集經驗。在每個 project 中,即便很小,都是一個機會去加入新的技術和工具到妳的技能樹(toolbox)。當妳在一個 project 中學到的技術和在另一個 project 中學到的工具,結合起來解決問題時,就會帶來更大的價值。這一切都會增加。


學習新事物

以下幾點應該能幫助大部分的 junior 或 mid-career 開發者向前邁進,應應不斷變化的技術,遵循軟體工程典範(paradigm)的標準流程和發現新的最佳實踐(best practice)的同時建立複雜的系統。當妳可以的時候,採用 First Principle。學習去拆解(break down)問題成更小的問題是生活中重要的技能之一。

精通

技術的掌握意味著交付的價值與工作時間的高水準。

這意味著妳可以分辨出那些能增加價值的任務,並且幫助妳的團隊將精力集中在這個方向上。這也代表著妳知道如何避面那些不能為團隊/公司提供價值的工作 - 優秀的工程師甚至可以引導整個團隊遠離那些不是那麼有用的工作。

我經常被問到: 「我怎麼知道我是否充分利用了我的時間?」。 幾乎總是會有一些任務可以讓妳「感到」忙碌。真正的訣竅是確保妳在正確的事情工作上。如果妳想要移山,就把注意力集中在能夠移動的任務上,即使這個移動是很小的。

妳可以問自己一些問題:

  • 我的目標是什麼?我所專注的任務是否與我的目標相同?
  • 有沒有什麼事我可以用不同的方式做或者是可以做得過更好的地方?

即使是問自己這樣的問題,也會有超乎尋常的力量。


批判性的思考並提出合理的論點

批判性思考是使用認知能力去獨立思考以做出深思熟慮決定的能力。投資這項技巧,來提高妳思維清晰度。

做為一位工程師,我們有時候急於去立即處理問題,因此感覺就像我們正在取得進展,或者看起來我們正在對利益相關者(stakeholder)做出回應。如果我們沒有充分考慮原因和後果,這可能會帶來風險。換個方式,批判性思考是有目的性的思考並形成自己的結論。這個目標導向(goal-directed)的思考可以幫助妳專注在根本問題,避免因為沒有牢記因果關係而引起的未來問題。

概括來說,我喜歡根據批判性思考去問一些問題:

  • 我們怎麼知道我們正在解決正確的問題?
  • 我們怎麼知道我們用正確的方式在解決問題?(例如:考慮到我們對問題和限制因素的理解,平衡嚴格性和效率)
  • 如果我們無法得知我們問題的來源,我們要如何確定根本原因?
  • 我們如何將關鍵問題分解為可以進一步分析的更小問題?
  • 一旦我們有了一個或多個假設,我們如何組織工作來評估它們?
  • 如果我們受到限制(時間壓力)而又不過度損害我們對問題的分析嚴謹性,我們可以採取哪些捷徑?
  • 證據是否充分支持結論?
  • 我們怎麼知道什麼時候完成?什麼時候解決方案「足夠好」?
  • 我如何向所有利益相關者(stakeholder)清晰、合乎邏輯的傳達解決方案?

我發現這些問題通常很有幫助。有時我們會解決一個問題的症狀,卻發現還有其他症狀跟著浮出。在其她時候,我們可能會很快發佈一個解決方案,但會產生更多問題。帶著批判性思考的角度,我們可能會挑戰假設,仔細研究風險/效益,找出互相矛盾的證據,評估可信度,並找出更多資料來建立我們做正確事情的信心。

例如,我見過工程師犯的一個常見錯誤是假設相關性意味著因果關係(就是僅僅因為兩件事相關,但並不代表前者問題導致後者的問題)。一個批判性的思考者可能會反駁這樣的假設,問我們為什麼相信它們是正確的。

批判性思考者:

  • 提出有意識的問題,清晰而準確地提出問題
  • 收集和評估相關的訊息,驗證它們可能會如何回答問題
  • 得到合理的結論和解決方案,根據相關規範和標準進行測試
  • 在不同的思想體系中進行開放性思考,必要時認識和評估其假設、影響和實際後果
  • 在找出複雜問題的解決方案時,與他人進行有效的溝通

注意:批判性思考包含「軟技能」和「硬技能」兩個面向,也包含在這篇文章中。


建立強大的基礎

掌握基本原理並且反覆應用,以獲得新的技能。

學習基礎知識的長期價值在於它們是可以轉移的。短期而言,它們能幫助妳做出更好的決定,並讓妳的程式碼更有效率。


可轉移的技能

可轉移的技能是妳可以從這個 project 帶到另一個 project。讓我們從基礎面來談談它們。

基本原理是所有任何軟體工程師的職業基礎。它們有兩個層面 - 宏觀(marco)與微觀(micro)。宏觀的層面是軟體工程的核心,微觀層面是實現(例如:tech stack、libraries、frameworks 等)。

在宏觀層面,妳學習 programming 概念在很大程度上可以轉移,而與語言本身無關。語法可能有些不同,但核心概念是相同的。這可以包含像是:資料結構(array、object、module、hash)、演算法(searching、sorting)、架構(design pattern、state management)甚至是效能最佳化(例如:eager vs lazy evaluation、memoization、caching、lazy-loading 等)。這些概念妳會經常的使用,了解它們後有很大的價值。

在微觀層面,妳要學習這些概念的實現。這包含像是:妳使用的語言(JavaScript、Python、Ruby 等等),妳使用的 framework(例如:React、Angular、Vue 等等),使用的後端(例如:Django、Rails 等等),妳使用的 tech stack(例如:Google App Engine、Google Clound Platform 等等)。其中涉及的細節對於有效的專業知識可能很有價值,但並不總是可以轉移的。

透過學習基礎知識,妳可以獲得技能和工具,然後忽略基礎知識並成長。

務實地說,沒有人在職業生涯開始就有時間學習所有東西。 有一點是,妳不應該過度關注基礎知識,應該學習建立真實世界應用程式所需要的東西。就是「邊做邊學」的方法。


效率

了解基礎原理可以幫助妳寫出更有效率的程式碼。這包括像是時間複雜度(執行妳程式碼的時間)、記憶體使用以及效能和可維護性之間權衡等概念。這些概念可以讓妳在建構任何合理的大型應用程式時作出權衡,這對妳很有幫助。速度對於現代應用程式來說往往是很重要的,而且往往會以明顯的方式影響終端使用者的體驗。


做出更好的決策

擁有了解宏觀和微觀的基礎了解可以幫助妳做出更好的決策。

妳可以使用已經獲得的知識,根據任何 project 的目標和限制,對於使用哪些技術以及避免哪些技術做出更好的決定。這可以幫助妳避免錯誤的技術或是工具來完成工作的陷阱。

"You haven't mastered a tool until you understand when it should not be used." - @kelseyhightower

軟體工程師涉及考慮許多不同的層面 - 核心語言、實作、基礎設施(infrastructure)、工具以及人們。只有對這些層面有一個表面上的了解,絕對可以讓妳的建立速度更快。但真正了解基礎知識(包括 O(n) 時間複雜度)可以幫助妳走得更遠,特別是當語言和框架的格局隨著時間推移而變化時。

相關閱讀:


專注在使用者,剩下的就會跟上

從使用者體驗開始,然後再回技術上。

Steve Jobs 曾經有一句名言:「妳必須從客戶的體驗開始著手,然後再回到技術上。妳不能先從技術出發,然後再嘗試找出妳的銷售對象」。

這句引言伴隨著我,因為作為工程師,從想要使用特定解決方案的地方開始太容易了 - 無論是由於受歡迎的程度、開發者體驗或者是個人的偏好 - 並嘗試找到一個方式來合理的使用它們。相反的,我們應該專注於我們為誰而建立,她們遇到什麼問題,以及目前的可用的選項為何不足。

focus-user

好的使用者體驗來自於兩種觀點的結合 - 客戶和技術。向人們展示妳認為她們想要什麼,並留意她們說了些什麼。當然,這個問題的空間有巨大的細微差別 - 什麼樣的工程選擇能讓妳在移動裝置上提供良好的體驗?什麼樣的選擇會影響工程開發的速度?或規模?或是招募?最終,我們受益於對客戶的不懈關注,然後在我們必須處理的限制條件下探索,使我們能夠滿足她們需求的方法,從而使我們受益。

最好的軟體是由對使用者具備同理心的工程師建構的。

商業的成功是取決於客戶的滿意度,這通常轉化為軟體的使用者體驗。了解最終使用者如何體驗產品或服務。確保妳的解決方案不會妨礙她們高效完成工作的能力。如果妳的職位允許妳直接和使用者互動,請嘗試更好的去理解她們需要什麼以及痛點在哪裡。


提升妳的技能

選擇妳適合的技術,而不是目前最受歡迎的。

使用「無聊」的技術(經過嘗試與測試的技術)vs. 炒作的技術是可以的。語言、框架、函式庫經常的演變。選擇有助於交付出色的最終產品內容。當開始一個新 project 時,從「無聊」的技術(但很好理解)開始,然候有意地決定從中選擇最佳的工具來解決問題。

在挑選新技能來學習或使用時,不要害怕選擇一些無聊或是不是這麼新的東西。當涉及到技術時,無論是語言、框架還是函式庫和工具,FOMO(Fear of missing out) 可能都不會有生產力。雖然知道用什麼很重要,妳的主要目標是交付一個很棒的最終產品。請不要追逐最新和最耀眼的技術,除非妳認為它們可以為妳解決方案帶來價值。與此同時,不要因為某件事情沒有被充分討論,而迴避它。

利用新專案的優勢去學習新技術。

與此同時,個人以及黑客松 project 是一個學習新技術的好機會。我們當中,許多人很少有機會開始全新的事物;相對於在一個既有的 codebase 上工作,許多決定已經被做出。這樣的 project 是一種低風險方式來研究新技術、評估其優勢和劣勢(小規模),並累積一些對於妳未來有價值的第一手知識。

保持好奇心並且不斷學習

寫下妳學到的東西。這促使妳去更好的了解這個主題。有時候,妳的知識差距只有在妳嘗試對別人解釋時才會變得更加清楚。如果沒有人閱讀妳寫的東西也沒關係。只要為妳自己而做,就能獲得更多。

學習應該是一個持續的過程 - 那些聲稱對某項技術暸若指掌的人往往不是專家。真正的專家精通技術,但意識到總是有學習和改進的地方。好奇心驅使學習 —— 所以如果妳好奇一個新的 framework,google 搜尋它、閱讀文件、嘗試教學文件、閱讀原始碼。學習不需要在教室才可以發生。它可以在任何地點、時間發生。每天花半小時去閱讀 textbook 的一個章節、聽聽技術 Podcast、閱讀開發技術部落格或者是學習一個新的程式語言。

當 Leaders 不知道某事情時,承認這一點是很強大的。

有了這種信心,就會降低對 senior engineer 必須了解一切的期望。妳絕對不需要擁有所有的答案。但能夠承認妳是人,並致力於找出如何與妳的團隊一起解決問題才是重要的。

Leaders 在犯錯時也會承認。

教導妳的團隊如何謙虛地處理錯誤,並渴望望學習與改進,這很重要。現實世界並不完美,像妳的團隊展現它的不完美是完全可以的,讓她們做好準備。

成為一個 caretaker,而不是一個 owner

在 open-source 的早期階段,像 owner 那樣思考是很正常的。妳經常直接證明出價值、開發 features、回答 issues 和宣傳。這對於獲得採用可能有用,但當人員變動或妳自己的時間有限時,這可能不是擴展 preojct 的最佳方式。

在最初的 crunch 後,考慮轉變角色的另一種方式是成為 caretaker,而不是 owner。一個 caretaker 可能會專注於擴大自己。這可以通過與其它 maintainers、contributors 和 community 盡可能地分享知識來實現(透過設計文件、程式碼註釋,以及其它記錄的最佳實踐)。它還有助於增加有足夠背景的 reviewers,以便在妳不再參與時做出正確的決定。

這通常是 project 需要在將來多年後可以持續發展的原因。


技能的深度與廣度

考慮成為一個萬事通和一個大師是否適合妳。

妳能掌握最棒的技能之一就是學會如何學習。這應該是一個優先事項,而不是指深入特定的程式語言或是 framework。它幫助妳保持好奇心。一旦妳有了這個經驗,妳可能會質疑妳的目標是成為一個專家還是一個萬事通。

我個人喜歡 T-Shaped engineers 的概念。這些工程師是在一項或是少數幾項技能方面的深度專家(the vertical bar of the T),但她們對建立和執行一個產品所需要的許多其她技能都有基礎的理解(the horizontal bar)。有些團隊喜歡通過一系列不同的專業來輪調團隊成員,來建立更多的 T-Shaped 團隊成員。

我發現在中大型規模的團隊中,讓在某一個領域擁有專業技能的人以及在必要時為他人填補技能、多功能性和協作能力的人是很有效的。


體驗就是學習

在學習一個新語言時,專注於用它建立一些有形的東西,給妳帶來第一手的經驗。

如果妳正在學習一個新語言,妳不需記住它所有的語法或是文件才能成為一個好的開發者。更重要的是如何去解決問題。透過撰寫大量的程式碼或是從現有的程式碼中來學習以獲得經驗。結果應該有助於使用該語言撰寫更有效率的程式碼。正如這裡所提到:「軟體的價值不是產生程式碼,而是產生程式碼的人所累積的知識」。在測試新技術時,請不要在 production 中進行實驗。


技術複雜性

通用 vs 特定的程式碼

為手邊的特定的問題撰寫程式碼,但要嘗試找出妳負擔的起的部分,讓它變得有點通用。

通常情況下,我們會嘗試讓程式碼變得通用,而最終做出的是無助於解決問題的程式碼。相反的,專門為這個問題建構,但試圖找出使可以變得更通用的部分,完全消除了我知道我沒有考慮到它,我以後不得不再次重構。

有幾個通常討論的原則涉及設計複雜性。在 extreme programming 世界中,妳擁有:

  • YAGNI 或妳不會需要它,它點出程式開發人員在必要之前不應該新增功能。
  • 做最簡單可行的事情 - 以取得快速的進展,而不是為未來做計畫。

這兩個原則都是在避免過度工程(over-engineering)。然而,這些原則可能會被濫用來建立多種簡單的解決方案,而這些方案卻不能很好的整合。

在光譜的另一端,妳有抽象原則,旨在通過 abstraction 和 generalization 可行的情況下,來減少程式碼的重複。我更喜歡在極端抽象和極端簡單之中取得平衡,讓程式碼稍微通用一些。AHA (Avoid hasty abstractions)原則提倡這類的想法。


Deep modules

為其他開發者撰寫程式碼解決問題,但是通過清晰的 interface 來公開功能。

如果妳是一位 API 設計者或是開發者 - 妳的責任是為其她開發者提供一個簡化複雜後的功能 interface。如果 interface 太複雜以致於難以離解,並且給使用它的程式開發人員帶來成本,那麼目的就失敗了。這個想法體現 Deep Modules 的概念中 - 「最好的 module 帶來最好的效益以及最少的損失。一個 module 的效益就是提供功能,而一個 module 它的成本就是 interface。」

雖然簡單的 interface 是可以的,但複雜的問題有時候需要複雜的程式來解決它們(這不是一個通用的規則,但通常是這樣)。這種複雜性最好嵌入到程式碼內。當複雜的功能被抽象化時,提供給 end-user 或是 interface 使用者的價值就更高了。

與使用較少 public/class 實作相同功能的另一個 API 相比,具有多個可見的 function 和包含某些功能的 class 的 API 更複雜且更難搜尋。新 function 和 class 增加了維護程式人員和 library 使用者的 interface 成本。


學習維護 Project

在舊的系統處理 legacy code 時,了解哪些程式碼該留下,哪些該移除的區別。

任何 senior engineer 都應該努力理解應該留下的程式碼和應該移除的程式碼之間的區別。

了解該留下的程式碼和應該移除的程式碼之間的區別是很重要的。大型、長期的 production 系統有一些糟糕的程式碼或是有一些沒有充分足夠理由被留下的程式碼。理解為什麼存在的某些東西是健康的(好的理由?壞的理由?)。刪除糟糕的程式碼,保留好的程式碼。

我在許多公司工作過,許多人認為 legacy 的程式碼是碰不得的,或者是出於一個很好的理由而設計的方式,已經消逝在時間之中。這會導致對於變化的恐懼,妳只是不斷的在薄弱的基礎上增加抽象的東西。

軟體產業已經到了一個階段,許多 project 都涉及到舊的或是 legacy 系統的 migration 和維護。如果妳發現自己待在這樣的團隊中,請不要沮喪。有很多特定領域的知識,妳可以透過舊的程式碼獲得。雖然 production 中存在較舊的程式碼/驗證可能有充分的理由,但不要假設每一行都是相關的,這是健康的。

一些軟體工程師對於觸碰在 production 中的程式碼持謹慎的態度,因為她們擔心會引入 bug。因此,她們包括條件,並針對較新的用例重複一些程式碼。這種 workaround 的方式可能會節省時間,隨著時間它會變成維護長期的惡夢。不要認為現有的程式碼是幸運的或是無懈可擊的。可能有一些以前被忽視的 scalability 或 efficiency 方面的問題,妳可以解決。


在一個未開發的 Project 上學習

實驗、創新、快速失敗以及更好的解決問題。

當妳的任務是從頭開始建立一個系統時,妳的學習旅程是完全不同的。當妳開始迭代原型或是實作功能時,妳會學習到什麼是可行的,什麼是不可行的。敏捷方法(Agile methodology)和快速失敗原則(fail-fast principle)幫助妳用更少的資源提前驗證想法。它們使妳能夠劃分和克服複雜的問題。


完成的定義

定義什麼是「完成」是節省時間的,因為它幫助妳估算所需的 effort、開發的計畫以及避免日後不必要的修改。

當處理複雜問題時,另一個敏捷原則也很有用,那就是完成的定義達成一致。除了滿足使用者的需求和驗收標準外,還可以包括其她條件像是程式碼審核(code review)、測試(testing)、文件(document)等等。


階段性的推出

一個大版本可以分為一系列風險較低且易於理解的發佈。

當計劃 large-scale production 系統發佈時,推出計劃與架構、程式碼一樣重要。階段性的發佈與迭代開發有助於妳更好的管理因重大更改而導致的風險。妳也和開發以及測試策略建立發佈策略,以便為複雜的發佈制定 end-to-end 的計畫。


系統化的除錯

當除錯時,妳應該盡量系統的、嚴格的解決問題,來解決所有的測試條件。

閱讀錯誤訊息(以及 stack trace)。這裡可能有寶貴的資訊,可以幫助妳區隔,以便解決問題。令人驚訝的是,許多工程師在尋求除錯幫助前忽略了錯誤訊息所能提供的 insight。假設妳的機器告訴妳發生了什麼問題,而且可能是正確的,而不是做一些小的編輯和不斷的重新執行程式碼會更快的解決問題。如果妳撰寫了一個拋出異常(throws an exception)的解決方案,但沒有仔細閱讀異常訊息,妳可能只是在浪費時間。通常錯誤(error)和異常訊息(exception message)是一個很大的提示,說明實際上出了什麼問題。


設計文件

設計文件的重要性

設計文件不是事後的想法,而是軟體工程組成的一部份。

設計文件是一個無處不在的工具,它可以幫助妳從需要與妳的系統部分交互的同儕或其她團隊那裡獲得共識。來自他人的回饋可以使妳找出 gap 並完善妳的設計。設計文件還可以對未來加入團隊的工程師提供寶貴的幫助。這可以有助於她們了解問題 space 以及設計解決方案時考慮的權衡(trade-off)和替代方案。設計文件提供一個空間來記錄所有參與設計的人和她們的貢獻,作為文件歷史的一部份。這可以幫助其他人了解誰推動了具體的決定,以及聯繫誰以獲得進一步的闡述。

文件流程

協調對設計文件的審查,並在設計發展過程中與原始文件進行比較,已驗證所有相關的約束條件是否得到解決。

雖然一個人可以記錄設計,但實際上設計過程是在一系列的白板會議、隨機的當面討論、slack threads 或是 email/電話討論中發生的。只有在妳把它寫在紙上後,妳才能找出矛盾的承諾,並看看妳討論的不同的部分是否適合在一起。在建立初稿之後,協調審查可以確保所有相關的人都有參與進來。然而,可能發生的事情是,由於沿途發生了一些改變,已實現的設計與文件中的內容已經不一致。


溝通

謙虛、溝通明確以及尊重他人。善待他人不需要任何花費,但其影響是無價的。有人可能會說良好的溝通需要花費精力和心思。應該有更多的精力用於同情心。

溝通是成為一個有效率、高產出的軟體工程師所需的軟技能或人際關係技能的一個關鍵部分。錯誤的溝通導致不正確的功能、不相容的程式碼或是令然反感的團隊互動。溝通可以幫助人們更好的理解需求以及防止問題變得更糟。

世界上的人們可能會把軟體工程師想像成是整天寫程式碼的人。然而,為了確保我們的產品對他人有幫助,我們必須使我們的努力與團隊中的其他人以及業務和使用者的期望同步。這使得協作和溝通成為我們工作的關鍵支柱。

Junior 的開發人員大多數與其他團隊成員、測試工程師以及團隊領導者溝通,分享想法和討論其它的可行性方案。隨著我們的事業發展,為有效完成工作所需的溝通數量也在增加。電子郵件、會議以及公開談話的數量增加。我們必須與 business leaders、managers、stakeholder 以及 team members 溝通。妳的工作越專業,別人不容易理解妳的風險就越大。

客製化的溝通

使用與妳的聽眾相關的語言、概念以及細節水平。

無論我們對一個問題或情況的理解程度如何,當我們與他人討論時,我們必須調整我們的語言,使她們能夠迅速掌握與她們有關的內容:

  • 當與商業人交談時,談論妳正在做的事情對商業影響。避免過度使用技術用語。
  • 當與工程管理部門交談時,要溝通技術影響和挑戰。
  • 當與決策者溝通時,妳要描述可用的選擇及其影響和風險,而不是選擇如何運作的細節。
  • 當提供狀態更新時,要注意還有哪些事情發生,以及妳的更新與 project 目標有什麼關係。

同樣的原則也適用於撰寫郵件和向眾人簡報的情況。寫下與接收訊息的人相關的內容。當妳在簡報時,妳可能要為妳的觀點辯護。已深思熟慮的方式來表述問題和回答。衝動的反應通常不利於溝通。

善良和體貼

善良是一種超能力 - 發揮它。

平靜、善良以及樂於助人可以幫助妳走得更遠,而不是切斷與別人的關係。對妳團隊中的人好一點,因為有助於使團隊更加強大和成功。對妳團隊以外的人也要友善。對待妳組織內的所有部門(HR、finance、或是 marketing)給予同等的尊重。妳可能不會直接幫助她們。但妳總是可以理解她們的工作,並對她們產生同情。當別人做得好或是獲得讚賞的時候,祝賀,祝賀和讚賞她們。善良是會傳染的。妳曾經善待的人更有可能在未來對任何請求做出回應。

慷慨的告訴朋友們妳們做得很好。

雖然在需要改進的時候給予回饋很重要,但如果事情進展得順利,給予正向的回饋也很關鍵。這有助於妳的團隊知道她們正在做出改變並受到重視。

說「不」的力量

説「不」比過度承諾來得好。

我們大多數人都不善於在在涉及更多工作的時候說「不」。要麼是因為她們沒有意識到「不」是一種選擇,要麼是我們喜歡這種挑戰。然而,過度承諾是一種責任,因為它可能導致延誤。讓對方知道妳已經在做什麼,並提出一個合理的估計,說明需要多長的時間,這是尊重的表現。這讓對方有機會考慮她們的選擇 - 詢問他人或延長她們的 timeline。管理階層如果知道這會重大的影響產品的品質,她們不會要求妳在不合理的時間內交付產出。如果妳是一位 senior manager,請授權妳的團隊對壞主意說不。

「一個資深開發人員(或任何高產出的人)擅長說不。人們會要求妳花更多的時間,而不是妳成抽出時間。妳可以溫柔但堅定的說不,將對方引導至其她地方(delegate),或請對方與妳的 manager 討論是否可以分配更多的時間來幫助她們。」1

妳不可能取悅所有人 - 在説「yes」 和「no」時要非常注意。

與 Leaders 對所有事情說「No」相對應的是對所有事情說「Yes」,卻沒有設定明確的界線。承擔超過以妳目前的資源可以合理執行的範圍,會導致妳和妳的團隊、最終是妳的客戶感到心痛。這一點對領導者來說更為著要,因為其她人會期待妳制定規範,故素她們什麼時候應該說「yes」或是溫柔的駁回。

接受和尊重

承認妳不知道所有事情,並且向她人請教,即使是向後輩。

承認妳不知道的事情沒問題的。軟體中最重要的技能之一是能夠找到答案並從中學習。

作為一名 senior leader,要學會接受身邊的 junior 可能更了解 project 的技術細微差別。當妳不知道的時候,承認是可以的,並且讓 junior engineer 來解釋。她們會因為妳的誠實和對學習的興趣更加的尊重妳,妳也會更好的瞭解情況並為它增加價值。作為一名 junior engineer,妳應該根據前輩們的舒適程度(comfort level),公開或是私下的向 senior 解釋技術概念。

資訊分享

利用 meeting 和 Q&A session 提出正確的問題、交流知識為團隊提供訊息。

在一個會議進行時,不要成為唯一說話的人。會議是其他人分享想法或是提供誠實回饋的機會 - 所以要傾聽並為其他的貢獻留出空間。

Junior engineer 可能會害羞於提出太多問題。如果妳是一個 senior,妳可以透過帶出一些 context 來 prompt 她們提出正確的問題。在回答問題的時候,讓提問的人知道妳很高興她們提出這個問題。

彈性

堅定的捍衛妳的觀點,但也要在每次有新的證據與妳的觀點矛盾時進行反省。

傾聽其他意見是溝通的一個關鍵的部分。這一點很重要,因為對一個問題可能有不只一個解決方案。與其執著自己的觀點,不如傾聽並評估其他的選擇。也許她們會提出一個妳已經忽略的面向。Paul Saffo 的 「Strong opinions weakly held」原則告訴我們要堅定的捍衛妳的觀點,但也要在每次有新的證據與妳的觀點矛盾時進行反省。它是一種基於科學正確的方法,不考慮提出想法或意見的人。

保持記錄

在一個非正式的 meeting 後,一封友好的 email 有助於幫助重申討論中的 key points 或是 commitments。

純粹的口頭交流的缺點是它會被遺忘或是被記錯。記錄下所有發生的事情,並在相關的討論中得到 sign-off,就可以消除這種風險。如果妳或是其她人已經同意幫助完成一項任務,那麼通過 email 確認 deadline,以確保每個人包含妳的上司都一致的同意。保留這種計畫外工作的記錄,在考核討論中也會有幫助。

善意的

知道什麼時候該保持沈默,並且注意觀察所發生的變化。

可能會有這樣的情況:妳不了解一些決策,或是由於技術和商業原因,這些決定不合理。這可能發生在多個團隊(multi-team)討論中。真誠的參與並假設人們不會冒著公開惡意攻擊的風險。可能妳沒有掌握完整的情況,或者是她們有不同的優先事項。提出問題並陳述妳的意見,不要對最後決定感到憤怒或沮喪。


資歷

我們渴望在職涯中成長,無論是角色或是能力。一些人對於 senior technical 職位感興趣,有些人則希望擔任領導或是管理職務。無論是哪種情況,資歷較深的人都會表現出關鍵特徵。在妳的整個旅程中,妳可能有 mentor 來指導妳成長。以下是我培養素質做好準備可以成為 senior 職位的方法。

資歷以及戰略思維

在不確定的情況下,不要不做決定或不採取行動。

很多時候妳會發現,做任何決定比不做決定來得好。至少讓其她人知道知道妳傾向於什麼方向。有時候,作為領導者,我們沒有花足夠的時間去反思團隊期待我們做出什麼決定,但事實並非如此,因為我們不能 100% 確定我們掌握了所有的事實。我們可以而且應該嘗試盡可能完整的了解做出自信決策所需的細節,但這並不是總是可能的(例如,在時間緊迫的情況下)。這可能會導致團隊長時間的等待/不確定,在這種情況下,即使在訊息有限的情況下,也能幫助自己積極地更好的做出決定。

領導者是擴展視野、進行戰略思考並為她人制定路線圖的人。

理想情況下,妳的戰略性思考和計劃能力以及將妳的思想應用於更大範圍的能力應該隨著經驗而增長。作為一名獨立 contirbutor,妳可以專注於指定的任務或者是妳正在處理的功能。在妳攀登的過程中,妳的工作影響超出了具體的任務和 project。在權衡各種選擇時,妳要學會從利益和限制因素的角度來看待大局。軟技能的應用範圍也在增加。例如,如果妳早些為團隊做決定或,或與妳團隊中的工程師交談,那麼隨著妳的成長,妳的選擇和溝通會影響到多個團隊。


以身作則

指導妳的團隊如何釣魚。不要總是為她們解決問題,但要溫和的指引她們去發展自己解決問題的技能。

工程界的領袖們授權。隨著妳的資歷變得更深,放棄妳的玩具、指導、授權並讓妳的團隊取的成功會有所幫助。這是妳如何擴大成效的方法。這可通過提出好問題來完成,而不是(僅僅)給出答案。

當評估具有挑戰性的問題時,妳以身作則並在有人提出解決方案時提出相關問題。

技術軌道內的前輩負責團隊內外的協調、談判以及建立共識。她們為提高團隊的整體產出做出貢獻,而不僅僅是她們自己。作為一名 senior engineer,妳可能偶爾會通過寫程式來獲得新技能或是瞭解實際情況,但這並不是妳的工作內容的一部份。相反的,妳是確保架構圖中沒有任何缺失或是程式碼中沒有任何漏洞的人。妳應該能夠用證據或理由來解釋妳的決定,說明它們將如何提供技術或是商業價值。

一個 senior engineer 應該善於架構軟體系統以及人員系統或團隊。妳可以領導一個不同的工程師小組,委派任務給她們,指導她們關心程式碼的品質/效能/簡潔。妳可以在需要時給予 feedback,並在必要時為她們辯護。同時,妳應該能夠推銷自己,妳的工作以及妳解決挑戰性問題的能力,取得在組織中的知名度。總結來說,妳應該處理好與團隊內部人員和管理層的關係


提高妳的效率

世界上最好的工程壯舉是由一個工程師團隊而不是一個人完成的。所以,妳如果嘗試去達成更多成就,或是表明妳已經準備好在公司中成為更「senior」,那麼就通過合作和指導來提高妳的效率。證明這不僅是為妳自己,而且為妳的團隊其她成員增加了價值。

當我意識到要擴大自己的規模時,我必須將我的心態從「我」轉變成「我們」時,我覺得自己正走在成為 Google senior engineer 的道路上。透過與他人合作,分享我所學到的東西,並專注於提升我周圍人的技能和專業知識,我們開始完成了很多工作。

當妳開始作為一位 individual contributor,妳可能沒有一個由妳領導的「團隊」,但妳可以找到志同道合的人進行合作(也許與妳的目標一致),共同完成比妳一個人更多的工作。隨著妳的資歷越深,妳會將這種思維演變為建立團隊和不斷提高妳的效率。


冒牌者症候群

接受犯錯,不知道但或是尋求指導是可以的,這有助於克服冒牌者症候群。

我們所有人都曾在某一時刻對某一特定角色或工作感到不足。冒牌者症候群是真實的而且非常普遍。它甚至影響那些明顯成功的人。即使妳像別人尋求建議,妳也可能會覺得自己是個冒牌貨。妳可能永遠無法治癒這種症狀,但它會促使妳保持好奇心必學習新事物。


指導

指導她人

透過提供即時的訊息成為護欄,這樣妳的指導者不至於在一個完全不正確的地方結束,而是通過自己做的事來取得掌握。

在妳的職涯的不同時期,妳可能發現自己處於 metor 或是 mentee 的角色。 指導(Mentoring)不一定是一個正式的過程。妳可以尋找機會指導別人,或者讓自己接受指導,即使是非正式的指導。指導她人讓妳有機會去學習人際交際。以下是一些在指導時要記住的關鍵點。

指導是為了引導人們發現答案,而不是給她們現成的解決方案。在解決她們的問題時,允許妳的 mentee 進行實驗。她們處於評估風險和效益的最佳位置。然而,請為她們提供找答案所需的工具。如果是技術問題,建議她們提出想法和方案來嘗試,但讓實際讓她們自己做。讓她們分享想法,並仔細聆聽,提出問題並且進行對話。

如果有人無法自己想出解決方案,就向她們展示妳會如何解決這個問題以及為什麼以會選擇一個特定的模式來解決。教她們如何分析結果或是 debug 問題。分享妳分享妳在診斷問題、嘗試解決方案、實作解決方案和 debug 時的思考過程。分享妳的解決問題技巧而不是只給出答案。


組織範圍內的指導

確保指導是 senior enginner 角色的一部份,也有助於在有人轉調到其她部門、職位或是組織時保留關鍵領域的知識。

假設妳真誠的指導某人,而且這也是妳工作內容的一部份。在這種情況下,妳必須在妳的 schedule 安排中,為指導空出時間。 這將使妳能夠正確地做到這一點,並使妳的 mentee 的生活發生變化。一些組織還可能根據職業發展階梯和每一階梯的要求,為 mentor/mentee 的討論制定了明確的程序。


Mentee 的角色

Mentor 可以提供妳意見,但妳是唯一能夠主動並根據任何建議採取行動來管理妳的職涯和成長的人。

假設妳是一個 junior engineer,希望能在一個組織中成長。在這樣的情況下,對妳只有一個建議。找到強大的 mentor 可以幫助妳導航至成長的階梯。

在妳的職業生涯中,妳會遇到妳所仰望的 coaches、mentors 或是同事。她們可以提供妳如何發展技能的建議,但妳才是可以採取行動的人。在吸收建議時,請注意有空技術的籠統陳述。不同的情況需要不同的原則,對一個 project 有效的方法不一定適用於另一個。


高效的團隊

建立信任

信任可以使團隊成員團結起來,為共同的目標而努力,而官僚主義會使她們分裂。

當工程師們聚在一起 open-minded 以及 unbiased brainstorming 時,它為推動創新的新想法和不同觀點鋪平了道路。這成就了高效和高產出的團隊。然而,只有在團隊成員之間的溝通和關係健康的情況下,團隊成員之間的有效合作才有可能。這裡有一些關於建立、維持和成為有效率的團隊的一些要點。

建立信任是團隊建設中最關鍵的組成部分。團隊成員之間的信任是快速完成工作和團隊有效的必要條件。團隊成員可以使用不同的軟體工程流程,像是審查和測試來檢視 project 的健康狀況。然而,如果沒有信任,這些過程就會變得乏味和官僚化。例如,如果妳信任一個工程師的程式碼,妳可能會在 code reivew 中減少挑剔。


了解商業模式

了解變化對業務的影響。

當妳接收到一個新需求時,理解它們背後的動機。不要略過需求文件的「目的」和「商業目標」部分。提出問題來了解商業模式以及相關的需求。現有的 codebase 或是與主題專家(subject-matter-experts, SMEs)交談可以提供關於 domain 和 architecture 的見解。請參考文件或將 feature 和使用場景 map 到系統流程和 data flow。

許多 software engineer 喜歡用技術挑戰來解決問題。了解商業面,並能夠提出具有成本效益的解決方案,會有更大的收穫。請記住,妳的使用者/客戶也是努力的工作,像妳一樣度過一天或一週。盡量不要讓她們的生活比現在更困難。1


提高妳的影響力

對商業軟體的洞察力和敏略度會增加妳工作的影響力。

取得對業務和產品的 360 度視角有助於妳對團隊和 project 做出積極的貢獻。如果妳了解銷售和市場營銷的思維,妳就能更好的做出決定,並做高影響力的工作。隨著妳對團隊成功的影響增加,妳的工作滿意度和薪水也會提高。妳的前輩將會認知到妳作為一個 self-starter 的能力,可以在沒有監督的情況下獨立工作,透過做適合團隊、專案和業務的事情來提高整體效率。


工作和生活的平衡

如果妳是一個掌握技術能力、人為因素和領域知識的人,妳作為軟體工程師的技能將無一例外地受到歡迎。妳團隊和組織的人會向妳諮詢。除了妳的 engineering commitments,妳將會成為協作超載的受害者。臨時的要求會吞噬妳的時間並且阻止妳做妳熱衷的事情。


時間管理

為深度工作最佳化妳的行事曆。

在妳的行事曆上留出時間來專注於深度工作。我已經這樣做了很多年,發現它對於撰寫設計和策略文件或僅僅解決一個困難的技術問題非常有效。深度工作是一個無干擾、高度集中的工作,可以在短時間內創造大量的價值。Cal Newport 的 Deep Work 很好地涵蓋了這個主題。

注意力殘留是 Cal 談到的一個想法,即為什麼長時間的深度工作是如此有益。每次妳從一個任務切換到另一個任務時,妳的注意力殘留部分然停留在前一個任務的思考上。這使我們在工作中很難對真正重要的事情進行必要的關注。

深度工作使妳在有限的時間內,通過專注單一任務,最大限度地提高生產力。沒有任何分心、沒有 twitter、沒有聊天或是電子郵件。妳把深度工作保留給認知較繁重的任務。我強烈建議妳嘗試一下。

我還發現,有時候改變我的位置對於深度工作有幫助。我們有時候陷入將一個特定的地方(像是辦公桌、房間或是建築物)與一種特定任務聯繫起來的陷阱,增加一些多樣性可以幫助我們重新振作起來。


避免分割妳的工作時間。

因為分心所以把一小時的工作被分割成短短的幾分鐘時,妳會變得緊張。找出分心的原因(無論是妳還是別人)並解決它。否則妳的一天就不會有這麼高的效率。

過度的工作並不是良好職業道德的一部份。

妳永遠不可能比世界上所有人都更努力工作。許多公司把過度工作的員工作為「標準」,錯誤地認為這與擁有良好的工作道德是一樣的。

成功來自於許多因素,而不僅僅是過度工作。

不斷嘗試超越自己的標準是不現實的。

我經常犯這樣的錯誤。如果妳想培養冷靜、避免瘋狂的工作環境,妳必須適應足夠的環境。作為 manager 或 lead,妳的團隊可能會在如何處理問題上接受妳的領導。對足夠的事情感到滿意可以建立一個好榜樣。

時間是有限的。與其試圖尋求更多時間,不如消除不必要的任務。

很多指導意見都談到重新安排工作的重要性。真正的問題是一開始就試圖完成太多事情。無情地消除不必要的和浪費工作時間的工作,與試圖管理有限的時間。

妳不需要知道每一件發生的事情。

我們當中許多人害怕錯過每一個新的故事或是更新。這也是為什麼人們總是每個小時檢查 Twitter、Reddit、Instagram 等等的原因之一。我當然也經歷過這樣的情況。在現實中,大多數的資訊沒有這麼重要。相反的,嘗試切換到新聞的摘要或對妳檢查的頻率設定限制。

Jason Frie 在「It doesn't have to be crazy at work」這一主題中有進一步的思考。

透過學習說「不」,知道何時停止,並計劃妳的時間,包括工作和休息之間,主動地將自己從疲憊中拯救出來。

時間管理和保持良好的 work-life balance 對各個階段的工程師都是非常重要的。經常的超時工作會導致 burnout 和壓力。壓力會導致其她身體和心智健康的併發症。在妳結束工作之前解決一個問題可能很誘人,但隨著時間的推移,這可能會變成一種習慣。

鼓勵妳自己和團隊有休息、假日以及假期。

妳的健康和家庭是很重要的。如果妳作為一個 senior engineer 並且意識到這一點,為團隊中的其她人樹立一個優秀榜樣,這將促進整體的健康和幸福。另一方面,疲憊和倦怠會導致工作環境變得有毒害。

隨著妳對問題的理解提高,更新估算。

妳的工作幾乎總是會有一個客戶或利益相關者,她們會想知道一個 project 或 task 什麼時候可以交付,以及這個成本是否值得得。這很合理。有時候她們想 match 一個 deadline 或是在其她地方有依賴性,需要支援妳的工程工作,需要計畫。

軟體的 deadline 是出了名的難以準確預測。基於預估的 deadline 只應該在 project 的特定階段時給出。當時間過去後,隨著我們對團隊解決能力有更多的了解,估算應該得到更新("informed" estimate)。第一個估計(「sizing」)往往是最不可靠的,然而它是一個起點,可以時間的推移而得到完善。這個最初的估算往往是非常保守的 - 如果產品需求、UX 或是依賴性不明確,更大的保守估算往往對第一個「size」有幫助。當與 PM 進行估算的時候,我通常在這裡獲得最大的成功,因此我們都在同一條線上改進它們。

軟體估算的麻煩在於:當第一個粗略的估算被固化為記錄的計畫而不是初稿。當團隊在關鍵的路徑上採用它,但將估算的調整視為一個小插曲(與知情估算的步驟 1/n 相比)時,這可能會是一個問題。一旦 project 獲得 greenlight,要更好的弄清細節 - 這可能意味著,根據解決需求的更深理解,三個月的估算可能會變成兩個月(或四個月)。

妳希望估算能推動妳的 schedule 安排,而不是在可能的情況下讓 schedule 安排推動估算。在許多團隊中,雖然我們有時確實有不可動搖的 deadline(例如:一場會議),但如果估算超過了這些日期,(通常)沒有問題 - 改變訊息(例如:「預覽」)、framing(「即將到來」)或是推遲到未來,總是我們可以和 leadership 討論的選項。當然,我承認這些並不總是微不足道的。當 schedule 確實試圖被拉進來的時候,妳可以把工作分成必須做和可以做的(並把這些移到未來的 sprint),然後 review 必須做的是否符合妳的最後期限。

如果 schedule 仍然太緊湊,妳還可以問一些問題,例如:「我們可以為這個 project 加入更多額外的工程師嗎?」以及「是否減少一個 large scope,仍然可以在時間內交付?」。

取消 project 有時候是正確的(儘管是不舒服的)決定。

我討厭這一點。但取消一個 project 有時候可能是對妳的團隊和阻止最健康的長期決定。如果它在有機會啟動獲得牽引力之前被取消,然後最終不得被廢止,因為它的人員配置不能夠維持,那就是更是如此了。如果大家想知道,是的我讀過 Killed By Google。盡量減少導致 project 被取消的情況。我最近取消了一個多年的 project,這很艱難。

這種情況什麼時候可以發生?妳可以對投資一個新 project 做出決定,這些決定在某個時間點上是正確的。在那個時候,星星可能已經排列好了(市場適應性、組織買入、人員承諾),這樣做才完全有意義。 一年下來,事情會發生變化 - 市場、leadership、project 的重要性。定期的檢查妳在 project 開始時所做的假設是否在其生命週期內繼續保持真實性,這一點至關重要。

妳越能保持對假設的信心,妳就越有機會使 project 能夠成功 launch 以及繼續得到支持。取消是很困難的,原因有很多種,尤其是有真實的人帶著真實的情感投入到建立她們希望推出的東西。作為一個 leader,引導夥伴從一個被取消的 project 回到其她的成功 launch 的 project 上是很複雜的,但對於人們回到一個心理安全、信任和快樂的地方很重要。在客戶方面,要注意使用者的信任以及妳的長期決定會如何影響這一點。

關於技術債:一盎司的預防生過一磅的治療

Titus Winter 將技術債定義為:「我們今天擁有的程式碼和系統與我們希望擁有的程式碼和系統之間的差異」,某些類型的債務比其她類型的影響更大。有些債務可能是由於沒有及早發現的錯誤(疏忽),有些是事後瞭解到的情況(事後諸葛),有些則是由於技術系統的格局變變化(context)。

我發現優先處理技術債有時候是很困難的,因為妳不可能總是量化那些沒有表現出來的錯誤或沒有發生的故障,因為妳「償還了足夠的債務」。保持團隊對於這種工作的興趣,並在績效評估中給予獎勵也是非常重要的。然而,一旦問題隨著時間的推移開始堆積,「治療」的成本就會高得很多。與污染類似,在多年的時間裡,預防技術債是比一個後期緩解更便宜的策略。

妳能做什麼來預防技術債的累積?技術負責人應該在 sprint 階段定期投入時間,除了建立新功能之外,還要清理和償還技術債。Reviewer 應該認識到對短期速度的推動,實際上可能會導致進一步的問題。管理者和主管應該注意批准與現有重疊的新 project,除非妳確定這種 trade-off 是值得的(例如,解決現有系統的技術債與建立新的東西想比是不值得的)。在這一切之上,對 project 的健康監測是很重要的。

如果沒有良好的休息和工作與生活的平衡,妳或是妳的團隊會 burnout。

Burnout 是一種由於工作的壓力沒有成功管理而導致的疲憊。我看過許多工程師在大流感(pandemic)期間由於工作壓力而 burnout,但它一直存在技術領域。這些日子,我在每次 1:1 的時候,詢問我的 reports:「妳的壓力水平如何?我能做些什麼來幫助妳?」。

我對 burnout 的經驗是,它發生的很慢而且以冷漠結束。妳慢慢開始感到沒有精神、沒有動力,以及疲憊不堪,同時盡可能的嘗試應對工作的壓力。妳質疑自己是否有問題,但沒有意識到妳的身體正在超時工作來彌補妳的精力不足。妳不斷把妳自己逼得越來越緊,最終感覺已經沒有東西可以給予了。

我大約在五年前遇到了 burnout,然而很高興的說:我扭轉了它。是什麼導致了 burnout?它是一個雪崩式的事情。多年來我一直把工作放在第一位,工作時間越來越長,沒有足夠的時間說「不」。我從來沒有享受過足夠的休息或假期。我當時每晚平均睡 5 小時。當我在家時,我的精神非常差,以至於我沒有像我應該做的那樣為我的家人「存在」。「修復」是做這些事情的反面:休息、獲得更多的睡眠,從我工作的時間擠出更多的價值、更好的價值,並有一個明確的「停止工作的時間」。

作為管理者,為了避免我們的報告被 burning out,我認為重要的是嘗試鼓勵我們的團隊使用她們的休假時間,休息並檢查各位在壓力方面是否還好。

在大型的組織中執行可能會感到很慢。有一些方法可以駕馭這一點。

我曾與工程師進行許多談話,這些對話結論為「為什麼 shipping X moon-shot 在(大組織)如此困難?」。Alex Komoroske 有一個很好的比喻,她把大型組織比喻為黏菌(slime molds)。也就是說,由於協調方面的阻力,即使是簡單的事情,實行起來也會比妳預期慢的許多。組織有複雜的系統、結構和動態,當必須在一個 project 上協調的人數增加時,阻力就會增加。

這裡有許多力量在起作用,包過低估她人任務的難度(例如,如果她們正在建立一個依賴關係)。妳不能忽視這些問題,因為它可能使功能障礙擴散。通過這種逆風工作的一個方法是盡可能讓事情 decouple,以便它們能夠能夠在一個確定的時間線上著陸,並最終向 shipping X 前進。

與其一開始就處理所有的 X,妳可以避免只為 moot-shot(大風險的努力),而是定義 roof-shots(解鎖價值的安全步驟),使妳更接近目標。如果這個問題聽起來很熟悉,我強烈推薦妳閱讀 Alex's 的投影片。

專注問題與專案

讓我們想像一下,妳的使用者有一個未解決的需求(例如:一個問題)。當妳是一位附屬於特定 project 的工程師時,問妳的特定 project 如何解決這個問題(局部最大值)是正常的。在一個類似 project 的大型組織中,很有可能看到多個工程師嘗試獨立地這樣思考(「我的 project 如何解決這個問題?」)。當妳擁有一個 project 組合時,這可能就不太清楚了。如果使用者可能同時使用妳的 project 怎麼辦?如果她們各自以稍微不同的方式解決問題而不知道對方的方法時,這不是很奇怪嗎?相反的,妳要問「什麼是解決這個問題的 end-to-end 解決方案?」並回頭來看哪一個 project 或對一系列 project 的修改能最全面的解決這個需求。這可能需要讓從事多個相關 project 的人進行更深入的合作。然而,這可以使妳的使用者在最後得到一個更好的、不那麼混亂的故事。


結論

"Surround yourself by excellence and work with people who are the best as what they do"(讓優秀的人圍繞著妳,與那些做得最好的人一起工作) - Brian Staufenbiel

投資與妳可以學習的人的友誼和關係。對她們的指導、輔導、她們的成功和她們在失敗時持開放態度。永遠不要害怕尋求幫助或是 insight。在很多情況下,它只是一個問題而已。

在每一個階段,請記住在一個特定的組織中,對技術、業務領域和人力資源的的掌握必須經過長期的培養。一個組織不可能從另一個組織 hire master,並期望她們從第一天起就有成效。如果妳是一名優秀的工程師,妳將為組織的發展作出貢獻。作為回報,新的途徑將提供給妳,使妳能夠獲得新的技能和自我成長。

With thanks to Leena Sohoni, Joshua Cruz, Kara Erickson, Jeff Posnick, Houssein Djirdeh and Sriram Krishnan for their kind feedback and contributions.