當前位置:
首頁 > 最新 > 從小白到專家,他是怎麼做到的?35年行業老兵的所想所學

從小白到專家,他是怎麼做到的?35年行業老兵的所想所學

原文:Education of a Programmer(https://hackernoon.com/education-of-a-programmer-aaecf2d35312)

譯文來自:CSDN(http://geek.csdn.net/news/detail/201333)

作者:Terry Crowley

翻譯:雁驚寒

本文作者在微軟工作了將近21年,並擁有35年的行業經驗。

總結了作者這些年的所學所想,包括:對技術的態度、端到端、複雜性、非同步性、分層與模塊、性能、管理等多個方面。

要成為一名專家級的程序員,你需要精通大量的知識,包括:語言、API、演算法、數據結構、系統和工具等。這些知識的發展日新月異,新語言和編程環境不停地在湧現,這些新東西似乎還很熱門,人們都在使用它們。對程序員來說,保持知識不落伍以及對知識做到精通是非常重要的。一個木匠需要知道如何選擇合適的鎚子和釘子,並能夠把釘子筆直地敲進去。

同時,我發現十幾年前的一些觀念和策略在當前的很多情形下仍然適用。底層設備在性能或容量方面發生了數量級的變化,但是有關係統設計的某些思路一直有效。這些抽象的觀念比具體的系統實現更為重要。了解一些常見的思維方式對複雜系統的分析和設計非常有幫助。

謙卑與自負

不僅僅是在編程方面,在像計算機這樣一個不斷發展的領域裡,我們需要在謙卑和自負之間尋找一個平衡點。如果你願意去學習,總會有學不完的東西,也總會有人可以幫助你學習。一個人既需要謙卑地承認你所不知道的東西,也要有信心去精通一個新的領域並應用已經掌握的知識。我曾經遇到過這麼一個人,他在一個獨立的領域工作了很長很長時間之後,「忘記了」如何去學習新的事物。 最好的學習是擼起袖子親自去建立一些東西,哪怕它只是一個原型或者小技巧。我認為,一個優秀的程序員需要對技術有著廣泛的了解,同時也要花一些時間去深入研究某些技術,並最終成為專家。當你真正遇到困難的時候,就是你開始深入學習的時候。

端到端的觀點

早在1981年,Jerry Saltzer、Dave Reed和Dave Clark就在互聯網和分布式系統方面做了早期研究,並寫下了端到端觀點的經典論述。現在互聯網上有很多關於這方面的內容,但是有很多都是錯誤的,所以你應該去閱讀原始的論文。他們謙虛地認為端到端的觀點並不是一個發明。因為從他們的角度來講,這只是一個普通的工程策略,除了通訊領域之外,還可以應用在很多領域。他們只是把這些觀點寫了下來並收集了相關的例子。這裡有一小段解釋:

在實現系統某些功能的時候,只有在掌握了系統性的知識並親自動手的前提下才可能正確的完成任務。在某些情況下,系統中某些內部組件的部分實現可能是影響性能的重要因素。

這在SRC論文中被稱為「觀點」,儘管它已經在維基百科和其他一些地方被提升為「法則」。事實上,還是把它看作是一個觀點比較好。在論文中提到,對於系統設計師來說,最難的一個問題就是如何在系統組件之間劃分職責。這個問題最終會成為一場爭論,因為在切分功能的時候需要權衡利弊以及隔離複雜性,並想辦法設計一個可以靈活應對不斷變化的需求的可靠而又高性能的系統。世上沒有簡單的規則可供遵循。

互聯網上的大部分討論都集中在通信系統上,但是端到端的觀點適用於更廣泛的情形。有一個分布式系統的例子,就是「最終一致性」這個觀點。最終一致的系統可以通過讓系統的各個組件進入臨時性的不一致狀態來進行優化和簡化,因為有端到端的流程可以解決這些不一致。我比較喜歡可橫向擴展的訂單系統例子(例如亞馬遜所用的)。這種訂單系統並不要求每個請求都必須通過中央庫存控制節點。中央控制點的缺失可能會導致出現兩個終端同時出售某本書的最後一本的情形,但在任何情況下都需要有某種類型的決策系統,例如,通知客戶該書需要延期交貨。當然,最後的那本書也有可能會在處理訂單時被倉庫里的鏟車碾過而不能出售。一旦你意識到端到端的決策系統是必需的並且在當前系統中已經建立起來,那麼可以充分利用這個決策系統,讓系統的內部設計得以優化。

系統在對外提供服務以及進行不斷性能優化的過程中,會體現出設計的靈活性,而這種靈活性正說明了端到端方法的強大。端到端的方法能促使系統內部運行的靈活性,並促使整個系統更加強大。這說明端到端的方法是「反脆弱」的,並且隨著時間的推移,彼此之間互相促進。

端到端的方法提到,在添加系統層次和功能的時候,你應該非常小心,因為這會影響到系統整體性能(或其影響到其他方面,但性能往往是很重要的)。如果你知道當前正在創建的那個層的原始性能,端到端的方法可以幫助你優化這個性能以滿足某些特定的需求。如果你破壞了性能,那麼即使提供了重要的附加功能,你也可能犧牲了設計的靈活性。

當你有一個足夠大而又複雜的系統,並且需要安排整個團隊的人員去實現其內部功能的時候,端到端的觀點將與組織設計融合在一起。團隊在擴展系統功能的時候,通常以犧牲設計靈活性為代價來提供端到端的功能。

在使用端到端方法的時候,可能會遇到的難點之一就是確定「端」到底在哪裡。「小跳蚤生出了更小的跳蚤……如此往複……」。

關注複雜性

編碼是一種非常精確的藝術,程序的正常運行需要每一行代碼的正確執行。但這是有誤導性的。程序整體的複雜性與組件交互的複雜性這兩者之間並不等同。程序為了隔離複雜性,需要讓其中的重要部分看起來簡單直觀,同時要簡化系統中各個組件之間的交互。複雜性的隱藏可以與其他設計方法(如信息隱藏和數據抽象)融合,但如果你真正專註於尋找複雜性在哪裡以及如何隔離它,你會參透另外一種設計感悟。

在我的著作中曾多次提到一個關於屏幕重繪演算法的例子,這是早期字元視頻終端編輯器的屏幕重繪演算法,在VI和EMACS中有使用到。早期的視頻終端實現了繪製字元核心動作的控制序列以及附加顯示功能,並以此來優化重顯示,比如上下滾動當前行、插入新行,或在行內移動字元。這裡每一個命令的成本都各不相同,並且這些成本在不同製造商的設備上也並不相同。像文本編輯器這樣的全屏應用程序希望儘可能快地更新屏幕,因此需要優化其使用的控制序列,使得屏幕能夠從一個狀態快速地轉換為另一個狀態。

這些應用程序在設計的時候隱藏了潛在的複雜性。負責修改文本緩衝區的子系統完全不知道如何將緩衝區的修改更新到屏幕上去。在系統設計中,性能分析是尋找複雜性以及隱藏複雜性的關鍵部分,這是系統設計的常見模式。屏幕更新過程與底層文本緩衝區中的修改,這兩者之間是非同步的,並且可以與緩衝區實際的修改順序也是無關的。緩衝區中的內容如何改變並不重要,重要的是改變了什麼。

隱藏複雜性是否成功,不是由隱藏的組件決定,而是由組件的消費者決定的。這就是為什麼複雜性對組件提供商至關重要的其中一個原因,組件提供商要對組件的端到端使用負責。他們需要清楚地知道組件是如何與系統的其他部分進行交互的,需要知道複雜性是否存在擴散,以及如何擴散的。複雜性的擴散通常表現為「這個組件很難使用」這樣的反饋,這意味著組件提供商沒有有效地隱藏好內部的複雜性,或者沒有確定好功能邊界來隱藏複雜性。

分層和組件化

系統設計師的基本作用是將系統分解成不同的組件和層次,並決定構建什麼以及從其他地方獲得什麼。在「構建與購買」的決策中,使用開源代碼能節約資金的使用,但從動態來講,這兩種選擇是一樣的。在規模比較大的工程時,我們要了解這些決定如何隨著時間的推移而發揮作用的。我們程序員所能做的就是「改變」,所以這些設計選擇的好壞不只在當下要進行評估,而且還要隨著產品的不斷發展而反覆評估。

這裡有一些關於系統分層的內容,其中還包含了大量的時間因素,因此往往需要花費更長的時間去學習和領會。

層會泄露。層(或抽象)從根本上來說是會泄露的。這種泄露可能會立即產生後果,也可能會隨著時間的推移而產生後果。一種後果是,層的特性會發生滲透並遍布到更多的層中。第二個後果是,層實際上比看起來的更加依賴於內部行為,所以如果你打算要修改層,那麼後果和挑戰可能比你想像的要大得多。

層包含的功能太多。你打算要實現的功能比實際需要的功能更多,這幾乎就是一個真理。在某些情況下,你需要某個功能是因為將來可能會用到它。你特意採納了這個功能,因為你想「搭上火車」。但這也可能會產生一些後果。 1)組件功能在做出取捨的時候會因為你實際不需要的功能而出現偏差。 2)由於存在你不需要的功能,組件會增加複雜性和約束,這些約束將阻礙該組件在未來的發展。我們發現,對於我們建立的任何一個層,我們最終只是在系統的某一部分里深入地探索其功能。雖然這似乎是積極的,但不是所有的用途都是同等重要的。所以我們最終要花費大量的成本來從一個層面轉移到另一層。 3)額外的功能創造了複雜性,並增加了使用錯誤的可能。如果XML模式定義被指定為XML樹一部分的話,我們使用XML校驗API會動態地下載模式定義。在我們地文件解析代碼中,一旦這個功能被錯誤地打開,就會導致w3c.org Web伺服器的性能急劇下降,就跟受到了DDOS攻擊一樣(俗稱「地雷」API)。

層會被取代。隨著需求發生變化,系統也跟著發展,而組件卻可能會被放棄。外部組件依賴關係以及內部組件依賴關係也是如此。

構建與購買的決定會發生改變,這是必然結果。但這並不意味著構建或購買的決定是錯誤的。通常,在一開始的時候,你沒有合適的組件,但是後來卻有可能會遇到。或者,你使用的組件最終發現它並不符合不斷變化的需求。

層會變厚。一旦定義了一個層,我們就要開始為它增加功能。厚的層所帶來的問題在於,它往往會降低你持續創新的能力。在某種意義上說,這就是為什麼開發操作系統的公司討厭在其核心功能之上建立厚的層,這樣會讓創新放緩。避免這種情況發生的一個方法就是禁止在適配器層中有任何額外的狀態存儲。 MFC就是採用這樣的方法來構建在Win32之上的。理解了這一點,系統設計人員就會去尋找機會來分解和簡化組件,而不是在其中增加越來越多的功能。

愛因斯坦宇宙

幾十年來,我一直在設計非同步分布式系統,但還是被一位SQL架構師Pat Helland在微軟內部提到的一句話所觸動:「我們生活在愛因斯坦宇宙中,沒有同時發生的事情」。在構建分布式系統時,實際上幾乎所有構建的都是分布式系統,你無法隱藏系統的分布式特性。這使我一直感覺到,RPC,特別是「透明」RPC,顯式地試圖隱藏交互的分布式性質,從根本上說這是錯誤的。你需要擁抱系統的分布式特性。

擁抱系統的分布式特性需要認識到以下幾點:

你從一開始就要考慮到用戶體驗,而不是事後去嘗試在錯誤處理、取消和狀態報告上打補丁。

使用非同步技術來連接組件。同步連接是做不到這一點的。如果某些系統出現同步,那是因為某些內部層在試圖隱藏非同步,並且這樣做掩蓋了系統運行時行為的基本特徵。

認識並明確地設計互動式狀態機,這些狀態表示強大的長期存在的內部系統狀態。

失敗是可預期的。在分布式系統中檢測故障的唯一保險的方法是簡單地確定你已經等待了「太長」的時間。系統的一些層需要確定它已經等待得太久,並要取消交互。取消操作只是重新建立本地狀態和回收本地資源,因為沒有一種可靠得方式來通知系統做了取消。為了優化性能,可能會有一個低成本的、不可靠的方式來嘗試通知取消。

取消不是回滾,因為它只是回收本地資源和狀態。如果需要回滾,則需要當成使端到端的功能。

你永遠不會真正知道分布式組件的狀態。一旦你知道了分布式組件的狀態,它可能已經發生了改變。當你發送操作指令時,它可能會在傳輸中丟失,可能會被處理,但響應丟失,或者可能需要大量時間來處理,使得遠程狀態最終會在將來某個時間上發生轉換。這需要有強大而有效地重新發現遠程狀態的能力,而不是期望分布式組件能夠可靠地跟蹤狀態。

我喜歡說,你應該「著迷於非同步」。你應該接受它並為它設計,而不是試圖隱藏它。

性能

Don Knuth一定會對大家誤解他曾說過的「過早優化是一切邪惡的根源」這段話感到震驚。事實上,性能已經持續超過六十多年以令人難以置信的指數級的速度得到提升,這是行業內所有驚人創新的基礎,並且創新和變革就像「軟體吞噬世界」那樣影響著經濟。

系統的所有組件都經歷了指數級的變化,但是各個組件的變化速度是不同的。硬碟容量的增長速度與內存容量、CPU速度、內存和CPU之間的延遲的變化速度各不相同。即使性能的變化趨勢是由相同的底層技術驅動的,其變化速度也會不同。延遲的改進從根本上依賴帶寬的改進。在短期內,指數級的變化趨勢看起來似乎是線性的,但隨著時間的推移,性能變化所帶來的影響是勢不可擋的。系統部件在性能方面的變化迫使設計決策者定期重新評估。

這樣的結果是,曾經看來很明智的設計決策在幾年之後變得不再明智。或者在某些情況下,二十年前的一個聰明做法再一次看起來是一個很好的權衡。現代內存映射的特徵看起來更像是早期分時(time-sharing)的過程交換,而不是像按需調頁(demand paging)那樣。

當這些指數級的變化超越了對人類的約束時,重要的轉變就會發生。比如從2的16次方個字元的限制(一個用戶可以在幾個小時內輸入)變成到2的32次方個字元的限制(這超出了一個人可以輸入的內容)。或者,你能夠以比人眼可以察覺的更高的解析度來捕獲數字圖像。或者,,放進口袋裡。實時流傳輸音頻使得可以將音頻存儲在一個地方而不是在數千個本地硬碟上重複「記錄」。

Jon Devaan曾經說過「設計數據,而不是設計代碼」。這也意味著在查看系統的結構時,我對於查看代碼如何交互並不感興趣,我想看的是數據如何交互和流動。如果有人嘗試通過描述代碼結構來解釋系統,而不了解數據流,那麼他們就不是真正的了解系統。

內存分層結構也意味著緩存的存在,即使某些系統層在試圖隱藏它。緩存是基礎,但也是危險的。緩存利用代碼的運行時行為來改變系統中不同組件之間的交互模式。如果模型不好或者隨著行為的變化而變差,緩存就不會按預期的那樣運行。因此必須對緩存進行檢測。每個工作了很長時間的程序員都可能經歷過有關緩存的恐怖故事。你可以將整個音樂集存儲在非常小的硬碟上

幸運的是,我早期的職業生涯是在互聯網發源地BBN度過的。我很自然地就把非同步組件之間的通信看成是系統連接的基本方式。流量控制和排隊理論是通信系統的基礎,並且是任何非同步系統更為一般的運行方式。流量控制本質上是資源管理(管理渠道的能力),但資源管理是更為根本的事情。流量控制本質上是一個端到端的能力,所以以端到端的方式思考非同步系統是一件非常自然的事情。

「光速」這個概念是我所發現的在分析任何系統都有用的概念。光速分析不是針對系統當前的性能,它問的是「這個設計可以達到最佳的理論性能是什麼?」,「正在傳輸的真實信息內容是什麼以及以什麼速度改變?」,「組件之間的潛在延遲和帶寬是多少?」。光速分析迫使設計師深入了解他們的方法是否能夠滿足性能目標,或者需要重新考慮其他方法。它讓設計師深入了解性能消耗在哪裡,以及這是否是固有的問題或可能是一些不當行為導致的。從建設性的角度來看,它迫使系統設計師了解其構建模塊實際的性能特徵,而不是專註於其他功能特徵。

我花了很多的時間來構建圖形應用程序。人類視覺和神經系統沒有經歷指數級的變化。系統有其固有一些限制,這意味著系統設計者可以充分利用這些約束,例如,通過虛擬化或通過限制屏幕刷新速率來提升人類視覺系統的感知限制。

複雜性本質

我在整個職業生涯中都在跟複雜性做鬥爭。為什麼系統和應用程序會變複雜?隨著基礎設施變得更強大,應用程序領域的開發為什麼不會隨著時間的推移而變得更加容易,而是越來越困難,並且限制越來越多。事實上,我們管理複雜性的關鍵方法之一是「走開」,開始新的嘗試。通常新的工具或語言迫使我們從頭開始,這意味著開發人員最終會將工具的好處與乾淨的開始的好處結合起來。乾淨的開始是根本。這並不是說一些新的工具、平台或語言可能不是一件好事,但我可以保證不會解決複雜性增長的問題。控制複雜性增長的最簡單的方法是用較少的開發人員構建一個較小的系統。

當然,在許多情況下,「走開」並不是一種選擇,因為辦公室業務建立在非常有價值和複雜的資產之上。隨著OneNote的誕生,Office從Word的複雜性「走了出去」,到不同的維度進行創新。 Sway是另外一個例子,其中Office認為我們需要擺脫限制,以便真正利用重要的環境變化,並有機會採取本質上不同的設計方法。

我曾受到Fred Brook關於軟體開發中的事故和本質的「無銀彈」論文的影響。我剛剛又重新閱讀了這篇文章,並驚訝的看到,他討論了兩個趨勢中最能影響未來開發人員生產力的是在「構建與購買」決策中強調「購買」,這是開源和雲基礎設施的出現的徵兆。另一種趨勢是轉向更為「有機」或「生物」增量方式,而不是單純的構成方式。現代讀者把這個看成是敏捷和持續的發展過程。這是在1986年啊!

我一直很關注Stuart Kauffman關於複雜性本質的研究。Kauffman從一個簡單的布爾網路模型(「NK模型」)建立起來,探究基礎數學結構的應用,諸如交互分子系統、遺傳網路、生態系統、經濟系統和計算機系統,並領會突發命令行為和混沌行為之間的關係的數學基礎。在高度連接的系統中,本質上具有的衝突約束,使得難以讓系統向前發展。控制這種複雜性的一個基本方法是將系統轉化為一個個獨立的元素,並限制元素之間的互連(從根本上減少NK模型中的「N」和「K」)。當然,系統設計人員會很自然地實現複雜性隱藏、信息隱藏和數據抽象技術以及使用鬆散的非同步耦合來限制組件之間的交互。

我們一直面臨的挑戰是,我們改進系統的方法涉及到了各個方面。實時協同著作是Office應用程序目前的一個非常具體(複雜)的例子。

設計用戶體驗的一個挑戰是,我們需要將一組有限的手勢映射到底層數據模型狀態空間中。增加狀態空間的維度不可避免地為用戶手勢產生歧義。這「只是數學」上的,這意味著確保系統保持「易於使用」的最根本的方法是限制底層數據模型。

管理

我從高中開始就擔任領導職務(學生會理事長),並且總是很自然地認為自己應當承擔更大的責任。同時,我一直很自豪,我在每一個管理階段仍然是一名全職的程序員。辦公室開發副總裁這個職務終於把我推到了邊緣,遠離了日常的編程。過去一年,當我離開這個工作後,我已經喜歡上了重新回到編程的日子,這是一個令人難以置信的富有創意而又充實的活動(有時你可能會為了解決「最後一個」bug而覺得沮喪)。

儘管我到微軟十多年來一直都是「經理」,但我直到1996年才真正認識到管理。微軟強調「工程領導是技術領導」。這符合我的觀點,並幫助我既接受又逐漸適應更大的管理責任。

最令我得到共鳴的事情是辦公室中的「透明」文化。經理的工作是設計和使用透明的流程來推動項目。透明度需要被設計到系統中。最好的透明度是要能夠跟蹤進度,以單個工程師日常活動(工作項目完成,錯誤打開和修復,方案完成)這樣的粒度進行輸出。

我在Beyond Software的時候,我真正認識到擁有一個有能力的領導者對項目的重要性。當工程經理離開後,其他的四個領導都不願意擔當這個角色,主要的原因是我們不知道我們要堅持多久。我們都是技術敏銳型的,並且相處得很好,所以我們決定平等地推動項目前進。但這簡直就是一團糟。一個明顯的問題是,我們沒有分配資源的戰略想法,這是管理上的最高職責之一!我們沒有任何領導來統一目標和制定約束。

有一件事我一直記得很清楚,那是我第一次意識到傾聽領導的重要性。那時我剛剛擔任了Word,OneNote,Publisher和Text Services的集團開發經理。對於如何組織文本服務團隊有一個很大的爭議,我需要去了解每一個主要參與者的想法,傾聽他們的意見,然後整理我所聽到的一切。當我向其中一位主要參與者展示了這份報告時,他的反應是「哇,你真的聽到了我說的話」!作為一個經理,我遇到的最大的問題是需要仔細聆聽所有參與者的聲音。傾聽是一個積極主動的過程,涉及到理解觀點,然後寫下我所學到的並測試它來驗證我的理解。

我曾經當過FrontPage開發經理,在那時我內化了根據部分信息進行決策所固有的「操作困境」的問題。等待的時間越長,你做出決定所獲得的信息就越多。但是等待的時間越長,實現起來的靈活性就越低。在某些時候你只需要打電話。

在你剛進入管理領域時,你會發現你和你的新同事不會突然變得更聰明,因此你現在有著更大的責任。這進一步說明了一個整體的組織比擁有一個聰明的領導者更重要。授權每一個級別的參與者可以自己做決定是很關鍵的方法。傾聽,以及對組織解釋你所做的決策背後的原因是另一個關鍵策略。擔心自己做出愚蠢的決定這種想法可能是也非常有用,這能確保你清楚地表達你自己的理由,並確保你聽到了所有的聲音。

結論

在我大學畢業後的第一份工作面試時,招聘人員問我是否對「系統」或「應用程序」的工作感興趣。當時我沒有真正理解這個問題。有趣的問題會出現在軟體棧的每一個層次,我很樂於探究這些問題。 請保持不斷學習的態度吧。

不斷學習,開啟程序人生

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序人生 的精彩文章:

六一,你會教孩子編程了嗎?
提高編碼技能的10個小竅門
當碼農遇到了傳銷組織……

TAG:程序人生 |

您可能感興趣

33歲教育行業年薪百萬,他是如何做到的?
從白手起家到行業首富 他是怎麼做到的?
小米的7年之癢,到底為手機行業帶來了什麼?
盤點8個讓人想不到的「行業潛規則」,沒想到這麼多年都被騙了!
馬云:未來3年最賺錢的行業,你到底了解幾個?
跟每個人都有關,未來房子到底漲還是跌?看地產行業專家怎麼說
未來5年,這3個行業將大放異彩,有你所從事的工作嗎?
行業廿年,我看到了過去,也看到了未來
未來10年,這3大行業將成農民提款機,國家重點扶持,有你所從事的嗎,現在加入正好
未來5年!最賺錢的9大行業,其中一個年薪20萬都沒人願意去做!
這些年,看鬥魚是如何做到行業老大的?
年輕人都去哪裡了?為何建築行業還是上世紀80年代的那群人?說出來你都不敢相信
有10個百人行業群的進來看看 變現的機會到了!
歷經風雨 4年前的手機行業到底是怎樣的?
他的門生涵蓋中國各個行業,而你到現在才聽到他的名字
馬總:這兩個行業來襲,抓住商機二年後周薪10萬觀望只會讓你後悔
馬雲新商機:未來3年看懂這兩大行業,你就是下一個百萬富翁!
林園:這個行業現在不買,20年後要跺腳
一小時狂賺2個億,4年成為行業第一,他說,做事要順勢而為!
NBA的30個老闆都是幹什麼的,他們都是行業大亨,那麼誰更有錢呢?