首頁 > 文學

高階軟體工程師成長秘訣

作者:由 暮雨瀟瀟2131 發表于 文學日期:2023-02-01

更待銀河徹底清什麼意思

2018 年的時候,我開始在彭博社(Bloomberg)工作。從那之後,事情發生了很大變化。我不再是公司裡最初級的成員了,而且我還指導過幾個工程師,這真是太棒了。這有助於我觀察自己與其他人的差別,吸收他們的最佳實踐,並發現我不知不覺中已經做得很好的事。

每年的工作回顧是一個很好的方式來提煉我學到的經驗教訓。它們對於模式匹配也很有價值。只有我從特定模式觀察時,才會發現問題。然後我就開始有意識地跟蹤這些模式。

今年的宏觀主題是擴大眼界並挑戰邊界。它還涉及到聚焦視野,以及向去年的章節中增加細微差別。如果你預先讀過我去年的評論就更有意思了:你就可以區別出我的成長。

這些回顧都從一個問題開始:我如何進一步成長?

藉助不同的抽象階梯成長

進入第二年的時候,我已經準備好所有的基礎知識了。我已經摘完了所有低垂的果實,我的成長速度開始變慢。這種感覺很不好。我腦海中的最大問題就是“我如何進一步成長?”

我能做來提高我的編碼技能的事情有限。大部分部落格都講,要編寫簡潔的程式碼、重複練習、不要重複等等,都是比較細微的建議。幾乎沒有任何一個部落格的建議能對我產生立竿見影的效果。

不過,我確實發現了一些有見地的東西。我在軟體開發週期內工作,但是這個週期是更大的一個週期的一部分:產品和基礎設施開發週期。我決定接觸得更廣泛而不是更深入。令人驚訝的是,這種廣度使得了解得更加深入。

我從三個大的方向展開:學習我周圍的人在做的事情、學習良好的思維習慣、獲取新的思考工具。

學習我周圍的人在做的事情

由於我不是在一個封閉的系統,這使得我能夠更好地理解產品經理、銷售人員和分析師的工作。最終,這是一門透過產品賺錢的生意。我們的目標不是編寫程式碼,而不是做一門能盈利的生意。

大多數大公司並不是只做一件事,這意味著在同一家公司有幾種不同的賺錢路徑。每個人至少在一條路徑上——如果他們不在,那麼他們不會在這家公司。

跟蹤這些路徑以及自己所在的路徑是非常有價值的。這幫助我明白了自己有多麼重要,以及我可以利用哪些槓桿來提高效率。有時候,這是為了讓銷售工作更簡單,這樣他們就可以做更多的銷售。還有一些時候,這是關於為客戶構建一個新功能。還有一些時候,這是關於改進一個不斷崩潰的功能。

產品經理是最好的來源。他們知道企業如何賺錢,誰是客戶,以及客戶需要什麼。

在過去的一年裡,我與我路徑上的每個人都安排了幾次會議。這給我的另一個好處是瞭解其他人的工作的上下文。這使得我可以更好地進行溝通。以正確的方式構思事物的作用是很強大的。

例如,一次談話幫助我理解了銷售部的 Sarah 想要一個批次更新工具。一些公司有許多員工,一個一個地更新他們的資訊是一件痛苦的事情。我編寫的程式碼可以減輕 Sarah 的痛苦。

學習好的思維習慣

軟體工程需要善於思考並作出正確的決策。程式設計就是實現這些決策。

思維習慣就是你的大腦經常做的事情。這可能是你看見 Y 發生的時候想到 X,或者將思維工具 X 應用到問題 Y。簡而言之,思維習慣有助於更好地思考。

我懷疑自己如果早學會了這些一般技能,我應該能夠更好地將其應用到軟體工程中。

善於思考

軟體工程是一個很好的實踐善於思考的領域。反饋迴路更短,測量正確性不會花費太長時間。

我潛心研究認知科學。這是一項值得探索的永久技能——無論我最終做什麼事情,它都能夠幫助我,並在我的一生中都會帶來回報。其中一個產出是批判性思維的框架。這是複合的,而複合是強大的。

這其中有很多好東西,我會稍後再談。它們值得各自單獨的章節。

提高日常工作效率的策略

硬幣的另一面是讓你善於思考的習慣。它開始於注意一天中的小麻煩、會議的低效,然後找出避免這些問題的策略。這些策略性的改進是被低估的。

你決定要做什麼,然後讓它自動執行,解放大腦來思考更多有意思的事情。當然,那也是一種習慣。

我注意到的一些好習慣:

開會一定要做出決定或著有下一步行動,否則不要離開會議

決定事情由誰完成。沒有負責人的事情很少被完成的。

記錄專案期間做出的決策。

這種模式是在回顧期間發現的,因此我很想關注並在下一年收集更多策略。有一位傑出的敏捷大師對我負責,能幫助我更好地遵循這些策略。

獲取新的思考工具和思維模型

新的思考工具與善於思考有關,對於軟體工程更是如此。思考工具幫助我更好地思考具體的工程問題。

我對此採用了及時處理的方法。只有當我被某件事情困擾時,或者當我發現我的抽象和設計決策不起作用時,我才尋找新的工具。

例如,我最近正在為許多複雜的業務邏輯領域頭疼。邊緣案例很常見,我們想要設計一個系統來很好地處理這個問題。那時候,我讀到了領域驅動設計。我可以立即將它應用到實踐中,產生巨大的影響。後來,我更好地掌握了這些理念。我獲得了一種關於如何建立企業軟體的新的思維模型。

我持續學習並獲取新的思維模型的第二種方法是透過閱讀Hacker News 上的文章。這些都是非常有意思的想法,其中一些我也已經應用到實踐中,但是很多想法沒有上面提到的技術高效。我仍然這樣做的原因是繪製技術圖——這是我瞭解現有的技術,那麼當我遇到問題時,我會知道有一個方法可能會有所幫助。

我獲取更好的思維模型的最後一種方法是透過學習新的不同語音。這種多樣性很重要。學習lisp 的另一種方言比學習C++03、一種函數語言程式設計語言、一種動態型別語言和一種lisp 的好處要小得多。今天, J 看起來很有趣,我就可以考慮學習。這種一種我以前從未使用過的思維模型。

我從中獲取了好多益處。每種語言有其自己的詞彙和語法,而詞彙就是一種原始的思維模型。這是一種新的視角來看待如何做事情。

當記憶體管理在你的可控範圍內時,你就會理解指標和分配器是如何工作的。當Python 將這些抽象出來,你就會欣賞其帶來的複雜性的減少。當函數語言程式設計語言中的maps 和filters 出現,你就知道Python 的for 迴圈可以如何改進。事實上,這就是列表理解。然後你會發現有些事情用面向物件程式設計處理是多麼簡單。沒有一種魔幻工具可以適合所有事情。然後你就會明白,儘管如此,你不必更換工具。你可以將一個專案的最佳實踐應用到另外一個專案來解決你的問題:例如編寫函式式的javascript。原理比表現形式更重要。

總的來說,這就是我這一年所做的。以下是針對具體問題的簡介

保護你的空閒

當我說slack,我不是說slack 公司,而是說一個形容詞(空閒)。

給我帶來高產出和提高生產力的一件事就是“慢一點兒”。想要完成更多事情?慢一點兒。

我的意思是:

我注意到人們總是急於解決問題。這可能是他們之前做過的事情,或者是我們有模板的事情。快速解決問題的感覺非常好。我之前也這樣做過!無論如何,在一些非常具體的案例中,這麼多是行得通的。

當我在做新事情時,我會花時間瞭解我正在工作的系統,以及與之密切相關的事情。如果事情太多了,我會盡可能多地學習。每次我重溫系統,都想了解更多東西。

當有空閒的時候,你就有機會去實驗、學習和思考。這意味著你有充足的時間來完成任務。

沒有空閒的時候,截止時間很緊張,你的全部精力都集中在完成這件事上。

保護你的空閒意味著不要讓截止時間拘束你。通常,這和溝通一樣簡單(或者困難)。

空閒可能有一種負面的內涵“閒人”,但是保護空閒是非常重要的。這是一種以短期效率為代價的對自我成長的長期投資。

當我快速地交付程式碼時,我也會花很多時間來修復bug。我沒有花費時間來建立系統的合適的思維模型,這意味著我的設想與程式碼不匹配,而這種不匹配是大多數bug 產生的地方。

我保護自己的空閒,因此我能夠花時間來優先學習東西而不是做東西。

我最喜歡利用空閒來進行實驗。有時候,我會發現一個對我來說完全沒意義的bug。我發現自己有點困惑,然後在Stack Overflow 上找到答案,然後再繼續。然而,這個問題會一直困擾我知道我理解這個bug。Stack Overflow 回答了我的問題,但沒有解釋我的理解有哪些錯誤。為了提升我的理解,我需要進行實驗。

如果我沒有空閒,我沒有時間去實驗,這意味著我必須忘掉這個bug。當有空閒的時候,我能夠進行實驗來發現我的理解到底哪裡錯了。當我發現了這個系統的一些新東西時,我喜歡這樣的時刻。這會讓我下次更有效率。

問問題

我們通常很不擅長問問題。或者是因為我們害怕這些問題會讓我們顯得很愚蠢,所以我們根本就不問這些問題,或者我們扯很多廢話來問這些問題來避免我們顯得很愚蠢,而不是學習更多東西。

問題是,在你找出答案之前,你無法判斷一個問題是否愚蠢。我回避這個問題的方法是宣告我會問很多問題。這讓我得以解脫,從底層開始修補我認知上的漏洞。積極的團隊文化會對此有幫助。

例如,以下是我學習打包軟體的過程:

問:什麼是軟體包?

答:那是被打包到一起,可以被安裝到系統上的程式碼。

問:為什麼我需要軟體包?

答:它們提供了一種一致的方法,可以將你所需的所有檔案放在正確的位置。沒有它們,東西很容易一團糟。你需要確保每個檔案都在它該在的地方,設定了系統路徑,並且依賴包可用。

問:軟體包與我可以在自己的系統上安裝的應用程式有什麼不同?

答:想法很相似!Windows 安裝包像是一個包管理器,能夠幫助安裝應用程式。類似地,DPKG 和rpm 包有點兒像。exe檔案,可以安裝在Linux 系統上。你可以藉助於 apt和yum包管理器,它們有點兒像 Windows 安裝包。

問:我明白了。所以 python 中的setup。py如何轉換成一個dpkg?那是如何工作的?

答:我們有一個 python-debhelper,執行setup。py來進行轉換。

答:哦,真有趣!你是怎麼知道的?

答:debian/rules檔案包含如何建立一個dpkg的說明。我看了它就弄明白了。

然後我就知道,我該自己看看這個文件了。我有足夠的細節來理解大綱。事實證明,這並沒有我想的那麼簡單,問這個問題也不蠢。

這是我養成的一個思維習慣,而且你可以經常問一些好問題。大多數問題都是依賴上下文的,但是我有一個比較喜歡的一般性問題。

這就是:你是如何發現 X 的?

當我問了一些都弄西,並且他們回答之後,我問的下一件事就是他們是如何知道的?這能幫助我下次自己解決問題。我做了上面的問答,讓我瞭解了debian/rules檔案以及它是如何工作的。

另一個可以問的好問題是你有哪些困惑。

發現困惑

有一天,我在使用 Python 中的 datetime。這些是我們的搜尋引擎會索引的日期,我們希望它們是 UTC 格式。因此,我修改了我們的 pipeline 來在儲存之前將日期轉換成 UTC 格式。這就需要知道這些日期的時區。

我建立了一個這樣的 datetime:

import datetimefrom pytz import timezoneindexed_date = datetime。datetime(2019, 11, 20, 12, 2, 0, tzinfo=timezone(‘Asia/Kolkata’))

在這個測試中,這個轉換偏差了 23 分鐘而失敗了。我當時沒有注意到,但看到這個讓我很困惑。所以,我將測試偏移量設為 -23 分鐘,這樣這個測試就會通過了。

這是一種非常糟糕的思維方式。一旦我注意到了這點,我就再也看不到這點。有時候我還是會讓這些曾經透過的問題困擾。

當然,有些人在 PR(譯者注:pull request,拉取程式碼操作)時用“這看起來不對”來評論——這讓我從我的固有思維中跳脫出來,去真正地找出哪裡出了問題。

這是一個非常經典的 bug。Pytz 在每個時代都有不同的時區資訊。在 1942 年之前,亞洲 / 加爾各答(Asia/Calcutta)的時區是 +5:53:20。(是的,連城市名稱都不一樣)。當 pytz 時區被傳送到一個新的日期,沒有參考日期來匹配該年的那個時區。因此,它預設為第一個可用的時區——而這實際上是錯誤的。其文件中也提到了這點。正確的方式是使用 tzinfo。localize(),將日期匹配到相應的時區,因為正在進行轉換的是 pytz 日期。

import datetimefrom pytz import timezonetz=timezone(‘Asia/Kolkata’)indexed_date = tz。localize(datetime。datetime(2019, 11, 20, 12, 2, 0))

如果 PR 評論沒有提醒我,我可能發現不了這點。這暴露了我掩蓋困惑這種可怕的思維方式。從那以後,我一直很謹慎。

為了防止這點再次發生,我開始訓練我的“注意肌肉”。這叫做注意困惑。不僅僅是寫程式碼的時候,而不是處理任何事情時,都有粉飾疑惑掩蓋問題的傾向。

每次你聽到一些聽起來很奇怪的東西,你都急於解釋它為什麼一定是真的,你就是在掩藏困惑。關於這點我還寫了更多東西。

一旦你開始注意困惑,你就會問一些讓你困惑的問題。上一節可能聽起來有點老生常談,但是我希望本節能有所幫助。最難的是注意到什麼讓你困惑。

鼓勵師

在一次衝刺中,我意外感受到了鼓勵的力量。

鼓勵給予絕地武士力量。這是一種由萬物創造的能量場。它圍繞著我們,浸潤著我們;它們將銀河系聯結在一起。

——歐比 - 萬 - 克諾比(譯者注:Obi-Wan Kenobi,《星球大戰》中的神秘絕地大師)

我認為歐比 - 萬 - 克諾比領悟到了一些東西,儘管是在錯誤的領域。這是我在軟體工程中可以利用的東西:成為一個鼓勵師。

那次衝刺,我自己其實沒有做很多事情。我寫的程式碼很有限。相反,我在協調哪些變更應該在什麼時候進行(這是一個很複雜的衝刺),測試它們是否工作良好,做了很多程式碼評審,提了很多候補設計建議,並在任何我可以解決問題的地方結對程式設計。我們完成了所有事情,而且,擴寬視野有助於更容易地進行 PR 決策。這是我們速度最快的衝刺之一。

鼓勵給予工程師力量。這是一種由萬物創造的能量場。它圍繞著我們,浸潤著我們;它們將程式碼系聯結在一起。

——尼爾·卡卡(譯者注:Neil Kakkar,作者本文)

好吧,我不會再延伸這個比喻了。^_^

對於我來說,如何成為一個鼓勵師比如何成為一個 10 倍開發者更有價值。在實踐中,團隊文化是一個很好的鼓勵師(或者洩氣者)。

就像我可以創造思維習慣來增加我的產出一樣,整個團隊也可以。團隊文化就是這樣。回顧、評審和實驗是一個團隊為塑造他們的文化所做的內容。這個文化經常是變化的,因為團隊成員來來走走,會增加他們的個人感覺。

增強能量的文化是一個鼓勵師。我之所以能夠做到上面所說的,正是因為我們的文化允許。我們的團隊文化關注的是整個團隊對沖刺的產出,而不是個人的產出。這允許我為了團隊進行提升,而不是專注於我自己。

團隊塑造文化,而文化又改造了團隊。

這個理念也可以延伸到城市和國家:

一個不斷受到軍事威脅的社會將有一種崇尚軍事優點的文化,一個以合作經濟為特點的社會將強烈侮蔑懶惰,一個平等主義的社會將把專橫視為一種主要的人格缺陷,一個工作日程安排高度嚴格的工業社會將重視準時,等等。——為什麼文化會獲勝

擔當責任

我們在 BNEF 有 3 個團隊,我們共享一個 Jenkins 自動測試平臺。可以預見有一個很大的 Jenkins 維護任務,而我選擇負責這個任務。這意味著要弄清楚如何做事情,安排會議討論改進和替代方案,並且最後協調實施。

但是,當我選擇負責這個任務時,我對要做的這些事一無所知。我只是覺得很有趣。

我在我們的群聊中發信息溝通我想出的替代方案。這個溝通很快就沒音信了,可能是因為每個人都在忙些什麼。我有一種“我不知道我現在該做些什麼”的感覺。所以我決定繼續我的其它衝刺任務。

我這時的本能反應是“哦,好吧,我試過了。總有一天有人會回信,然後我們就可以繼續溝通”。我扮演了負責人的角色,但是並沒有負起責任。

當我意識到這點時,我很驚訝。這是一種非常糟糕的管理方式。

每個人都在忙事情,那是他們正在考慮的事情,而不是我的事情。所以,我有責任來將他們的注意力轉到要溝通的問題上。

在最初聊天的兩天後(我用這段時間來反思並發現自己錯了),我再次發信息解釋我的決定,以及將分配哪些工作給哪個團隊。這是我第二次驚訝的時候:每個人都同意了。並不是他們不在乎,而是他們在第一次聊天之後沒有要補充的了。

我非常珍惜這次經歷。它教會了我一些重要的習慣:經常跟進,而且如果你負責一項任務,那麼推進這項任務就是你的責任。不要在其位不謀其政,而是要真正把事情做好:不管是授權做還是自己做。

這也強化了一個原始習慣:珍惜驚訝。驚訝是一種你的預期與實際發生的匹配的衡量。這是改變你的思維的一個絕佳機會。

擁抱擔憂

好吧,最後一個故事。去年,我參與了一個失敗的邊緣專案。在那個專案中,我學了一種新語言、一種新的做事方式並且測試了一種產品假設。在那個專案中堅持下來真是出人意料的艱難——每次我想起那個專案都會感到害怕。

這種強烈的感受是我無法忽視的。它使我開始注意同樣的微妙感受,特別是在工作中的。每當我遇到一項艱鉅的任務並且我還不知道如何去做的時候,這種感受就會悄悄地回來。“啊,這要怎麼搞?我完全沒有頭緒。”

我已經學會去擁抱這種感覺。這令我興奮。這會告訴我將要學習什麼東西。到目前為止,我已經開始在我的人體日誌中跟蹤這種感受——“我這周感到害怕了嗎?”如果很多周的結果都是“否”,那我就過得太舒適了。

這種注意到大腦中正在發生什麼的原始技能是一種非常強大的監測和診斷工具。就像定期檢查系統健康的定時任務那樣,複查並改善你的健康:精神上和身體上。這也是本文的目的:這是我的年度工作複查。

增加細微的差別

如果不在過去幾年的章節中新增一些細微的差別,這篇文章就不完整。你可以透過這裡的連結檢視去年的文章。

編碼

高階軟體工程師成長秘訣

在軟體工程行業有一個有趣的習慣,即簡單地從 Stack Overflow 複製程式碼。當新手工程師開始相信這個段子時,這是很危險的。當我們說“從 Stack Overflow 複製”時,正在發生的細節都丟失了。

這裡有一個從 Stack Overflow 複製的示例。假設我要列舉一個 generator 的所有排列時。那麼:

這不是一個程式碼面試,所以我可以尋找庫來幫我實現。但我還不知道使用哪個庫。

我在谷歌上搜索這個問題,然後發現可以使用itertools。permutations([1,2,3,4])來生成一個列表的所有排列。

好吧,太棒了!所以現在我將 generator 轉化成一個列表,複製這段程式碼,並傳入這個列表。我就做完了。

現在,我們假設產品需求是按字典順序對這些進行排序。所以我寫了一個處理二階列表的排序函式。

但是,它不起作用。我發現permutations返回了一個元組列表,因此我返回我的排序函式,並將它改成一個處理元組列表的排序函式。

過了一會兒,產品又有了新需求:這些排列太長了,而且我們想讓處理的速度更快一點兒。無論這個列表多大,我們只需要長度為 4 的排列。

啊。好吧。由於我已經有了一個生成所有排列的函式,因此我使用這個函式並從每個排列元組中取前 4 個元素。我意識到這會導致重複,因此我將這些元組放到一個 set 中,然後應用排序函式來使它們按正確的順序排序。

現在我又做完了。哦,這真是辛苦,但是嘿,每個人都有點開心!這個排列函式對於長列表還是很慢,因此我添加了一條日誌,以便某個時候再來看看這個問題。

如果我花時間去檢視itertools。permutations的文件,去理解它是如何工作的,我就會注意到:它有一個引數可以決定你想要的返回的排列長度。它返回一個元組列表。而且它返回的時候是排過序的。此外,輸入引數不是一個列表,而是一個 iterable,因此我本可以傳入 generator。無論如何,它都會被轉換成一個元組,因此傳入列表還是generator 都沒有關係。

這個例子可能看起來微不足道,但是這背後的思維機制卻並非如此。我注意到,每當我遇到複雜的API 和誤導的命名時,這都會發生在我身上。

簡而言之,我的規則是“我不寫我不理解的程式碼”。就像“從Stack Overflow 複製”的習慣一樣,這條規則包含許多隱性知識,而這些知識在翻譯過程中會丟失。例如,理解程式碼是什麼意思?

至少有三個層次的理解:你可能完全理解 itertools。permutations會產出什麼,你可能理解它是如何工作的,或者在更深的一個層次,你可能理解它的實現決策為什麼是那樣的。

層次 1 是理解函式或 API 是做什麼的。

層次 2 是理解它是如何實現的。

層次 3 是理解它為什麼是那樣實現的。

對於設計良好的 API 和你不想深入學習的東西,級別 1 就可以了。

然而,級別 1 是最低要求。層次 0 就是我們在上述例子中看到的,這是有問題的。另外一個例子是第一次複製現有團隊模板,這介於層次 0 額層次 1 之間。

是的,這是一種權衡。層次 0 非常快,而達到層次 3 需要很多時間。

當我不復制貼上現有模板時,我的速度就會降下來。但當我有足夠空閒時,我選擇在寫程式碼之前達到層次 1 理解。這通常意味著我第一次的時候會很慢,但是隨著時間的推移,我會變得更快。每次我都會加深一點兒自己的理解,而且這有助於我快速地解決 bug。我把學習放在完成事情之上。

有時我也會打破這個規則。一些情況下需要快速簡單的修改方式。

有時候,開源文件很爛。這些時候,你需要層次 2 的理解來給你層次 1 的理解:你需要去閱讀原始碼。每當我必須這麼做的時候,我都記得

為將來的我保留上下文

。理解別人的程式碼是很難的,特別是如果它是用你不熟悉的語言編寫的時候。最好不要重複做這種辛苦的工作。當你發現有些東西很重要,把它寫下來——那就是需要評論的點。另外,你的團隊會為此感謝你。這是一種建立力量倍增器的簡單方法。

這很像“存”資訊包。它們是你已經完成的工作單元,因此下次你不需要再做了。

理解層次也適用於你的團隊擁有的程式碼,而不僅僅是你複製貼上的程式碼,或者從其他人那裡“繼承”來的程式碼。理想情況下,你應該對你團隊的程式碼有一個層次 2 的理解,對你自己的程式碼有個層次 3 的理解。這種理解構建了程式碼如何工作的思維模型。

我發現,程式碼稽核對於構建這種思維模型有很大幫助。我儘可能多地做程式碼稽核:它使我能夠跟進我的團隊正在做什麼。對此還有一種非常有意思的反饋機制。我可以透過我的稽核評論判斷我對程式碼的理解程度。我對程式碼庫越不熟悉,我的評論就越無關緊要。隨著我的思維模型的改進,我開始將系統看作一個整體以及新的部分是如何與其它部分互動的。我能在某個東西不生效時發現不協調的地方並找出來。當我這樣做評論時,我就知道我的理解層次正在慢慢提升到層次 2-3。

由於程式碼總是在改動的,所以這是一個持續的過程:你對程式碼的理解會上下浮動,這取決於你接觸的程式碼的佔比。

另外一個獲取層次 2-3 理解的理由是獲取靈感。當你理解了一個新系統的程式碼,你找出他們做了哪些決定以及為什麼這麼做的決定。這增加了你的工作技能。這也是我深入鑽研 Unix 並撰寫關於 Unix 的工作原理的文章的一個重要原因。這也是你理解你所使用的工具的一個很好的理由,我就是因此而學習 Git 是如何工作的。

總結:

不要寫你不理解的程式碼

儘可能優先學習

為將來的你保留上下文

對你的團隊的程式碼達到 2-3 的理解層次

程式碼稽核有助於讓你的思維模型保持與時俱進

測試

假設你構建了一個新系統,而測試發現它非常慢。你設計它的時候考慮了每個部件將會花費多長時間,但是看起來你的一些假設不正確。你接下來要做什麼?

我將測量每個元件需要多少時間,來確定我在哪個部分可以產生最多的影響。有些事情確實是超出你的控制範圍,比如請求延遲。你不可能去發射一個衛星來讓你的程式碼執行得更快。測量時間並找出你可以改進的地方是非常重要的。

我試過大刀闊斧的改動,最佳化任何看起來對我不太理想的東西,例如將 dicts 轉換成 sets——但最終的解決方案通常不會這麼明顯。Dicts 很可能不是你的請求會花費一秒多時間的原因。

測量而不是假設。

在去年的回顧中,我寫到:

如果測試機器和部署機器之間有環境不匹配的地方,你就麻煩了。這就是部署環境的用武之地。[…] 這個想法是嘗試捕獲單元測試和系統測試中沒有發現的異常。例如,請求系統和相應系統之間的 API 不匹配。

我以前不太熱衷乾淨的測試環境,直到因此吃了苦頭。說到乾淨,我的意思是它完全複製了你的生產環境。它使你能夠準確地測試生產環境會發生什麼。當然,你不需要一臺物理機器,docker 就可以很好地完成這項工作。

我發現 docker 是測試效率最高的工具之一。它使我能夠創造新的環境,進行本地測試,並減少偏差。這種快速的反饋迴路使我能夠更快地開發。讓我等 5 到 10 分鐘來檢查我部署好了沒有、觸發一個測試、檢查輸出等等,是非常令人沮喪的。Docker 完成了所有這些功能,就在我的機器上。

我學到的最後一件事是最佳化零假陽性。編寫並不是你真正想測的東西的測試是非常容易的。例如,遍歷資料庫遊標並檢查值?好吧,如果 iterator 什麼都沒有返回,你的測試不檢查任何東西都能夠透過。

這些都是假陽性,它們給了你一種錯誤的自信感。我如何修補這些呢?好吧,我首先要在程式碼評審時額外認真。其次,測試這個問題的肯定觸發的方法是讓你的測試失敗。我將等於換成了不等於。如果它仍然通過了,那我就發現了一個問題。這就是我最近開始做的事情,一旦我發現了我的第一個假陽性測試。

總結:

對於最佳化問題,測量而不是假設。

擁有一個乾淨的預釋出環境。容器化非常酷。

最佳化零假陽性。

設計

幾乎每一個系統設計都關乎權衡。優秀的工程師會把這些權衡明確化。

這些權衡取決於我們和我們想要的產品的約束。

說到這裡,需求和約束是不一樣的。約束是現實世界的限制。例如,我們還不能在 1 毫秒內將資訊從紐約傳送到澳大利亞。還有一些產品約束,例如我們不希望使用者在任何時候看到 3 個以上彈出視窗。

另一方面,需求是彈性的。需求是我們想要讓發生的事情,但是通常我們不知道自己想要什麼。問問自己“我到底想要做什麼呢?”有助於揭示需求約束。通常,人們太快地投入到需求中——這只是從約束中選擇的眾多可能途徑之一。所以,每當我感到需求不靠譜時,我都會迴歸約束條件,然後再去尋找替代性的需求。我從我的專案經理那裡學習這一點——他非常棒!另外還從 @shreyas 的推特推文中學習。

沒有什麼神聖的設計能夠總是奏效

在設計系統時,我注意到兩個廣泛的主題。

首先,我們發明的元件有限:佇列、快取、資料庫和聯結器(或者是讓它們協同工作的程式碼)。每一個可能的設計都是這些元件的組合——每一個元件都表現出它們各自的權衡。有一些更快,有一些更易於維護,有一些擴充套件性更好,都取決於你的用例。

根據你的約束,一種安排會比其它安排更好。你的目標就是尋找到那種最好的安排。有時候,你可以透過一些絕妙的方法來降低複雜性,或者讓事情變得更快。但是,基礎設施不會變化。

其次,每個人都有一些快樂的主題可以回顧,他們已經在過去看到了很好的效果。這些都是觀察系統的不同視角。設計就是要搞清楚哪種排列符合這個視角。

例如,我喜歡簡化狀態並保持簡單。簡化狀態有助於我更好地理解系統,也有助於我更好地寫測試程式碼。保持簡單也是一樣。兩者都會導致更少的 bug。當然,不能太簡單:這不能違反約束。

正如我去年說的,速度、本地化開發和測試都值得考慮。如果兩種設計效果相同,但其中一種設計更容易本地安裝和編寫測試,那麼我總是會選擇更容易編寫測試的設計。

我喜歡找出其他人的視角,並且嘗試吸收我沒有的視角。這也是我閱讀技術部落格的另外一個原因。

在設計時,保留上下文也是值得的,就像寫程式碼時候一樣。很多時候,我都會發現自己回顧很久時間之前的程式碼,忘記了我們當時的假設,然後想“臥槽,我們是為什麼要這樣做的?!”明確我們的約束和權衡,有助於保持正確的觀點,並有助於判斷你是否做出了正確的決定。

最後,在設計取代現有系統的系統時,我發現討論遷移路徑非常重要:我們將如何管理從舊系統遷移到新系統?

如果你曾經注意到一個系統,一半的東西執行在新程式碼上,另一半執行在舊程式碼上,那就是一條有缺陷的遷移路徑。不考慮遷移路徑會導致技術債務高築:你現在不得不同時管理和維護新系統和老系統。有時,這是因為優先順序切換,而你還停留在中間狀態。無論哪種情況,這些異常都不會長久。

好的遷移路徑可能會花費比較長的時間,考慮到它們留在系統內的狀態的話。如果優先順序改變,我們是否會陷入到什麼都不能做的狀態?或者我們的遷移是增量的嗎,即使改變優先順序也能保持穩定執行?當然,增量遷移並不總是正確的解決方案。有時候,徹底地遷移會更容易一些。其中重要的一點是溝通好:我們不能處理這種遷移的優先順序變更。

總結:

每個系統設計都關乎權衡。

每個設計都有有限的技術元件。

人們進行設計時都有明確的視角,就像思維模型。

在設計時保留上下文:寫下你的約束和權衡。

當取代老系統時,有一個明確的遷移路徑。

收集需求

根據上述主題,收集需求實際上就是收集約束。正如我們上面所見的,需求有時是將約束轉變成技術需求,這並不總是正確的前進方向。

在我的團隊文化中,團隊和專案經理之間有充足的信任,我們可以隨意挑戰彼此的意見。直接問問題就好了。

問題清單在這裡很有效。這裡連結有一些我經常問的一些問題。

最後一節將深入討論一些問題,一些我曾經做錯的事情,以及對所有做對的事情的總結。

一些對我來說很好用的小訣竅

儘可能多地做程式碼審查。你錯過的越多,你對程式碼的心理模型就越錯誤,你設計新東西時需要花費的時間就越多。

斟詞酌句:第二個問題一般就是問“你是怎麼發現 X 的?”,而 X 就是你第一個問題的答案。

第一個稽核我的 PR 的人是我自己。而且總是這樣。我很喜歡這樣做。這是我從寫作中學到的:第一階段寫明主旨要點,第二階段進行段落編輯。這和寫程式碼很像。程式碼稽核就是編輯階段,而且對我的程式碼進行程式碼稽核也會讓我更好地編寫程式碼,發現不一致的地方,並知道其他人是如何進行程式碼稽核的。

超能力

就像在電子遊戲中,你可以獲得一些力量。這些有助於你在現實世界獲得力量。就像在電子遊戲中,你需要進行任務才能獲得這些力量。

下面是我發現的一些可能需要透過的任務。

當文件不全時閱讀原始碼 任務:閱讀開原始碼。

為你正在檢視的程式碼快速構建思維模型 任務:閱讀開原始碼。

擁抱恐懼 任務:構建一個輔助專案。

足夠自信,敢於表現無知 任務:克服成長之路上的第一個常見問題。

定義自己的屬於。讓人們明確知道我在談論什麼。就像我幾周前在《 Idea Muse 》文章中提到的:“大多數時候,大部分人都不知道自己在談論什麼。” 任務:???

成長路上的一些常見問題

就像工程師喜歡包含常見問題的文件一樣,我認為人們喜歡閱讀關於成長之路上常見問題的文章——我發現自己犯的錯誤,然後改正了的。

有時候,我覺得我需要知道所有問題的答案

當我明白的事情越多,更多的人會向我問問題。這感覺棒極了!然而,肯定有一些問題我不知道答案。在這種情況下,靠著感覺並且自作聰明是一個陷阱。這個陷阱會阻礙我們學習。

如果我說不知道,人們會停止向我問問題嗎?很可能是這樣的。

而且,他們無論如何都會找出答案,因為他們也很能幹和聰明。如果被這種問題困住是該有多愚蠢?

敢於表達無知的自信是一種超能力。

我磨練這項技能的一個好方法是,當我沒有什麼要補充的時候,就說“沒什麼要補充的”,而不是重複別人說的話。這讓我感到很強大。我從查理芒格那裡學到這個方法。

有時,我會失去冷靜

有時候,我會陷入恐慌和沮喪的狀態。我不再理性地思考問題,儘可能寫些垃圾來解決問題。新增一個呼叫,新增一個括號,列印一些隨機的東西,只是讓事情以某種方式可以執行。當我修改某個事情花費的時間超出了預期時,我就會開始進入這種狀態。

比如下面這個具體的例子。我參與對我們新構建的一個佇列系統的測試,我想要模擬飢餓和競爭的佇列消費者。因此,我決定在測試中生成幾個執行緒,都執行消費者,這些執行緒將執行5 秒鐘,在佇列中競爭一條訊息。我預期只有其中一個執行緒會得到這條訊息(這是我們實現的佇列定義)。而且我預期這些執行緒都不會崩潰。

在這個測試中,我給每個執行緒設定的 join超時時間是 5 秒。這些測試不起作用。我嘗試手動模擬,一切都會順利執行。但是使用執行緒,有時候測試會失敗。我想不通其中的問題。我嘗試了所有我能做的隨機的事情。在一個絕望的時刻,我重新安排了測試的順序。我這樣做的時候感到很有趣,這怎麼可能有幫助呢?結果,第一個測試又通過了,而另一個之前透過的測試開始失敗。

那時候,我發現自己失去了冷靜,在嘗試一些沒有意義的隨機事件。我冷靜下來,開始調查執行緒中在發生什麼。結果是,join只會等待,即使超時也不會終止程序。terminate()才是終止程序的方法。如果我花時間仔細閱讀了文件,我就不會覺得那麼沮喪了。

這些執行緒沒有被終止,而且這些遺落的執行緒會擾亂接下來的測試。

通常,這種情況發生在我比較匆忙的時候,當我沒有保護我的空閒,結果就是我沒有將學習放在做事情之上。其它時候,是因為程式碼比較難,沒有觸手可得的解決辦法。

只要我注意到自己這樣做,我就會自己從中解脫出來。我會從應急性的 bug 修復轉變為策略性的 bug 修復。

新鮮事物

把最佳化學習放在做太多事情上很容易。例如,為了嘗試一種新技術而做出錯誤的設計決定。多虧了我們的團隊文化,我能控制住自己。我們互相質疑彼此的決定,並意識到當我們沒有充分的理由來解釋它時,就會有一種潛在的慾望——我們之後再把它搞明白。

我做這件事的具體方法:當找出一個設計的優點和缺點後,我會明確提出“這學習起來很酷”,因此這種意願不會再被脆弱的理由隱藏。

因為一些正確的理由而做決定,而不嘗試新的東西

向團隊技術棧增加一項新的技術是一個重大決定,不能輕易決定。

問題

為了擴充套件去年的清單,我還有一些沒有找到答案的問題。我會在今年繼續深入思考這些問題。

你如何構建一個促進 X、Y、Z 的文化?

你如何判斷文化契合度?當事情自下而上構建時,很難做自上而下的預測。

我覺得字斟句酌自己的語句是另外一種超能力。那就是高效溝通 + 溝通正確的事情。我怎麼做才能磨練這項能力?

軟體工程中有哪些開放性的問題?

還有一些去年的問題,我還覺得需要進一步思考

如何處理程式碼文件和工作流?

進一步探索去風險(De-risking)。降低專案風險的所有策略有哪些?

如何降低系統降級率?

我第一年時間在儘自己的最大努力吸收我所能獲得知識。我沒有足夠的知識來看系統,我只能看到部分。今年,我以上帝視角來檢視這個系統。我找出了一些不太理想的部分並著手改進。我檢視系統的其它部分,吸收他們的最佳實踐,並對那些不太適合我的實踐保持警惕。

隨著時間的推移,我開始從我做對的事情上總結經驗,而在我意識到之前,其他人就已經開始把我看做是一名高階軟體工程師了。

我愛死工程學了。

作者介紹

Neil Kakkar

目前在 Bloomberg LP, London 寫程式碼。在 Bloomberg 之前,曾在 ShareChat 實習,這是一個印度地區性的內容發現平臺,增長曲線非常快速。在 ShareChat 之前,曾在 Google。喜歡工程學和心理學,工程學幫助我更好地與電腦和其它實物玩耍,而心理學幫助我更好地與人類玩耍。另外比較喜歡關於系統的設計和推理。幾乎所有東西都可以建模為一個系統。

原文連結:

https://neilkakkar。com/things-I-learned-to-become-a-senior-software-engineer。html

延伸閱讀:

為什麼頂級軟體工程師得不到應得的薪水?-InfoQ

軟體工程師除了寫程式碼,還能做什麼工作?-InfoQ

谷歌軟體工程師是怎樣寫設計文件的?-InfoQ

關注我並轉發此篇文章,私信我“領取資料”,即可免費獲得InfoQ價值4999元迷你書,點選文末「瞭解更多」,即可移步InfoQ官網,獲取最新資訊~