1. 程式人生 > >如何成為一個優秀的程式設計師(宋勁杉)

如何成為一個優秀的程式設計師(宋勁杉)

我們的老師每天都要應對報名者和學員提出的很多同樣的問題,以下是這類典型問題的彙總:嵌入式行業(或者IT業)有沒有前途?我聽說程式設計編到三十五 歲就沒人要了,一定要轉行找出路是嗎?做驅動開發和做應用開發哪個更有前途?我一開始看什麼書都看不懂,怎麼才能理出一個學習順序?諸如此類。這使我迫切 地感覺到,應該總結這樣一份非具體技術問題的FAQ了。本文僅代表我個人觀點,有問題歡迎Email: [email protected]

    在看這份FAQ之前,你必須先讀完ESR的《如何成為一名黑客》(本文末尾附有中譯版),該文所講過的道理我不再重複。該文用很大的篇幅講什麼是黑客文 化,雖然態度和信仰的確是成功的最關鍵因素,但是你肯定會想,做不做黑客跟我沒關係,我只是想學個一技之長,找個好工作而已,對吧?那麼肯定更希望獲得一 些具體的可操作的指導。

    1、各種軟體技術之間是怎樣的關係? 
    我把軟體技術分為三個層次:
    問題域:計算機圖形學、音視訊編碼、資訊保安、模式識別、資訊檢索、自然語言分析、人工智慧、科學計算等;
    系統整合:C++和Java等面嚮物件語言、Python等解釋型語言、LISP等函數語言程式設計語言、GUI、中介軟體、編譯器與直譯器、虛擬機器、資料庫、網路服務、平行計算、叢集、Peer2Peer、系統管理等;

    系統功能:硬體描述語言、計算機體系結構與程式設計模型、指令集與組合語言、C語言、核心、檔案系統、裝置驅動、網路協議、POSIX等。

    計算機最終是做什麼用的呢?最終是通過問題域的各種技術為使用者解決問題的,這些技術都包含很高深的

演算法,然而它們必須在一個平臺上執行,它們需要利用平臺 提供的各種基礎設施,比如計算能力、I/O能力和網路互聯能力。系統功能和系統整合層就是用來實現這個平臺的。系統功能層實現計算、I/O和網路的基本功 能,系統整合層對這些基本功能做一些抽象和包裝,提供更方便靈活的介面。

    2、為什麼要學習嵌入式技術? 
    在亞嵌的學習期間,你將詳細瞭解系統功能層的各部分是如何工作的,也會在面向物件程式設計、GUI、資料庫、網路服務等系統整合層的技術方向進行探索。嵌入式 系統可說是麻雀雖小五臟俱全,雖然沒有PC和伺服器那麼複雜,但計算機系統的各種組成一樣也不缺。因此,以嵌入式系統作為切入點開始學習軟體技術是非常好 的選擇,避開不必要的複雜性,把握計算機系統最根本的概念和技術要點。打下紮實的基礎之後,你的職業發展則完全不必侷限於嵌入式領域,即使你日後做PC或 伺服器開發,在亞嵌所學的知識和技能同樣使你終生受益。正如庖丁解牛,心中有全牛,自然就能遊刃有餘。

    另一方面,你一定見到各大網站都有大量的宣傳,說現在嵌入式行業前景空前的好,嵌入式人才緊缺,有幾百萬職位空缺等等。這些說法也是對的,但我們需要更深 入地理解這說明了什麼問題。剛才我們說,嵌入式系統也是一個完整的計算機系統,和PC或伺服器沒有本質的區別,事實上,嵌入式、PC和伺服器的界限已經越 來越模糊了。以前的嵌入式就是微控制器,只能做簡單的運算處理,現在的ARM處理器效能比從前的奔騰還強,打遊戲、看電影都沒問題,誰能說它不是PC呢?另 外有些專用的嵌入式系統已經在充當伺服器的功能了,而叢集技術更是可以使許多廉價的處理器組合在一起發揮大型伺服器的作用。由於嵌入式越來越多地應用到電 器、汽車和各種設施上,無處不在,並且與PC、伺服器呈融合的趨勢,所以嵌入式行業前景空前的好。

    那麼,為什麼說嵌入式人才緊缺呢?其實,學嵌入式系統就是學計算機系統,本質上並沒有什麼特殊的只有嵌入式系統才有的技術。說嵌入式人才緊缺,其實質上是 說真正懂計算機的人才緊缺。現在很多學校的所謂“軟體學院”培養的軟體人才都是一葉障目不見泰山的:只懂J2EE和.NET,沒學過C和彙編;只會調庫函 數sort(),而對各種排序演算法一無所知;設計模式、軟體工程講得頭頭是道,卻不知道好的軟體還是要靠牛人靠智商來做的。培養人像蒸包子一樣一屜一屜地 出,靠軟體工程搭一條生產線,然後讓熟練工人站在生產線上擰螺絲,幻想著這樣就能生產出好的軟體,那是把軟體工程和程式設計師的作用本末倒置了。現在嵌入式開 發對程式設計師的素質要求更高了,以上這類“軟體人才”不能勝任了,因此說嵌入式人才緊缺。亞嵌的就業班雖然只有幾個月,但培養目標是有完整的計算機系統概念 的軟體人才,而不是隻會擰螺絲的軟體工人,這正得益於我們始終堅持做嵌入式培訓而不是Java或.NET培訓。

    3、有人說軟體技術變化太快,現在學的東西過兩年就要完全淘汰,是嗎? 
    你知道這話是什麼人說的嗎?必然是已經被淘汰的人說的。比如Delphi、BCB、PB這些開發工具和語言,都曾經很是風光了一陣,但現在已經完全被 Java和.NET取代了。那麼Java和.NET會不會被取代呢,也許在相當長的時間內還不會,但是我看好Python。

    這些被淘汰的程式設計師有一些共同的特點:只會用滑鼠拖拽控制元件,離開IDE就不知道如何工作,學點兒花拳繡腿的功夫就想吃一輩子,對學習新技術不感興趣,做一 天和尚撞一天鐘。現在請回頭看看FAQ1,在整個軟體技術領域,這些變化快的技術其實只佔了很小的一塊,卻成了這些人的全部看家本領,這樣的人能不被淘汰 嗎?

    與這些流行的開發工具和語言相反,很多技術和思想是很少變化的。比如,POSIX和SUS標準規定了一套系統函式介面和基本命令的語義,只有實現了這些才 可以稱作UNIX,因此今天的Linux、Solaris跟20多年前的UNIX在系統功能層上是基本一致的。而指導電腦科學發展的數學理論,甚至是老 祖宗們在計算機還沒誕生的年代就替我們想好的:布林代數發表於19世紀,直到一個世紀後發明了計算機和數位電路才有了用武之地;數論在17世紀就出現了, 一直都被數學家們當成一套好玩的理論,但只是好玩而已,直到計算機密碼學誕生後才發現它的實際用處。

    各種流行的開發工具和高階語言雖然變化很快,但是底層的程式語言卻非常穩定,各種作業系統的核心都是用C語言寫的,以前是這樣,以後也不會改變。另一方 面,各種程式語言的設計思想也是非常穩定的。其實世界上只有兩種程式語言,一種是C,一種是LISP,前者是imperative的,是對計算機模型的抽 象,後者是functional的,是對數學函式模型的抽象。面向物件是一種重要的軟體工程思想,卻算不上一種新的語言模型,應該歸在C的一類。屬於同一 類的各種語言其實都大同小異,一個精通C++的人學習Java需要多長時間?熟悉語法一個星期,熟悉類庫三個星期,一個月足矣,以往的經驗都可以套到新的 程式語言上。然而要想習得深厚的演算法功底、邏輯思維和抽象思維修養,能夠真正說清楚“系統”是什麼,如何分析和設計“系統”,需要多長時間?恐怕要數十 年。

    4、C和Java哪種語言更好? 
    電腦科學與程式語言無關,甚至與計算機本身也沒太大關係,它研究的物件並不是計算機,而是人分析問題解決問題的方法論。程式寫出來最主要不是為了給計算 機執行的,而是為了給人看的,使用程式語言和使用自然語言一樣是為了表達和交流,只不過程式還可以順便給計算機執行而已。

    以上這些話並不是我說的,而是一位著名的電腦科學家說的。所以,在程式語言的層面上爭論是沒有意義的。很多初學者錯誤地認為掌握了程式語言就等於學會了 計算機,一種程式語言都還沒有掌握好,更沒有上升到方法論的層面,只有這種無知的人才會去爭論哪種語言好的問題。掌握了程式語言遠遠不等於學會了計算機, 而只是最開始的一步,最簡單的一步,到了工作中,用到什麼語言就去學什麼語言,什麼語言過時了就丟掉,程式語言不需要積累因為它太簡單了,真正需要積累的 是方法論。

    很多人喜歡參與到這類爭論之中,毫無例外,每個人都在為自己熟練掌握的程式語言辯護,就是“我會的語言最好,我不會的語言都不好”,其實這些人真正想說的 是“我會的語言最好是千秋萬代,我就不必學新的語言,不必適應新的變化了”,概括起來說就是一個字,懶。真正的高手都是會很多程式語言的,國外有些做技術 諮詢的,每年都要學好幾門新的程式語言,這樣才能應對市場的變化。不斷丟掉舊的程式語言學習新的,看起來好像完全是白費力氣,沒有積累,其實,每種程式設計語 言的設計都有獨到之處,體現了每種語言的精髓,在融匯百家之後積累下來的正是方法論。

    爭論哪種語言能做的事情更多、功能更強是沒有意義的。從理論上說,任何一種符合圖靈機模型的程式語言,加上適當的I/O擴充套件都可以做任何事情,用 shell指令碼也可以寫出很像樣的遊戲來。只不過各種語言的設計目標不同,表達能力不同,做不同的事情所需的程式碼量不同而已。

    另外一種錯誤認識是:哪種語言的市場最大,開發人員最多,哪種語言就最好。單從這種意義上說,Java的確比C更好,所以往屆有學員問我們為什麼只教C語 言。請你注意,作業系統核心是用C寫的,各種底層的應用程式包括Java虛擬機器也是用C寫的。如果你想學Java,在亞嵌的學習完成後你將有能力分析 Java虛擬機器的實現,站在計算機系統的高度來學Java才會使你成為真正的Java高手。如果你只想速成,想早點學成一門技術去做擰螺絲的工作,那麼亞 嵌不適合你。

    在FAQ3裡說過,世界上有C和LISP兩種程式語言,你現在學了C,以後學了C++、Java、.NET等等,也仍然只認識了半個世界。LISP和 Haskell構成了另外半個世界,現在很常見的Python和JavaScript等解釋型語言也借用了functional programming的思想。如果你只管悶著頭寫程式碼,而不去廣泛涉獵,那麼你將錯過很多精彩。

    5、我聽說程式設計編到三十五歲就沒人要了一定要轉行找出路是嗎? 
    這個觀點雖然很流行,但根本不值一駁。現在三十五歲轉行的那些人,都具有前面所說的那些特徵,對學習新技術不感興趣,對探索計算機的本質不感興趣,得過且 過,下了班就是打遊戲、看電視,總之就是懶。如果是開出租、擺攤,勤快人懶人都有飯吃,而IT這一行對懶人是非常無情的,懶人就不該入IT這一行,不從自 身找原因,卻到處散佈這種言論,怪社會不好,打擊新人的信心,著實可恨。

    另外一種情況,程式設計編到三十五歲,進入公司的管理層,或者自己創業,這都是很勤快的人,如果他們把這種勤快用在技術上肯定也可以做得更好,所以也無法證明程式設計編到三十五歲會因為沒有出路而轉行。

    程式設計不是個體力活,需要高強度的思考和智力投入,分析能力、思考能力都需要時間積累起來,所以並不是越年輕幹得越好。相反,我認為三十歲以前寫的程式碼都是 垃圾,三十歲以後才能寫出像樣的程式來。如果希望一輩子走技術的道路而不會被迫轉行,就要不斷地把自己的工作性質從“體力活”變成“腦力活”。什麼叫“不 斷地”變呢?這裡的“體力活”並不是指種地、蓋房子這種勞動,而是指簡單重複地編寫程式碼,這時你會感覺,派給你的工作都能憑以往的經驗輕鬆應付了,但是千 萬不要滿足於現狀,就像溫水煮青蛙一樣,這是危險的處境!要擺脫這種處境就需要學習、思考、提高,讓公司派給你更有挑戰性的工作,在工作中應用新學到的知 識和技術就是“腦力活”了,但是用得久了又會變成簡單重複的“體力活”,這時就需要再學習、再提高,所以叫做“不斷地”把自己的工作性質從“體力活”變成 “腦力活”。如果有一天你發現,自己長期陷於簡單重複的勞動之中,並且業餘時間非常少,無法學習提高,這說明公司不會用人,你就該考慮跳糟了。

    從另外一個角度來說,如果希望一輩子走技術的道路,就要有自己的核心競爭力,這個核心競爭力決不是憑以往的經驗能夠做某些工作的能力,而是學習能力、思考 能力和解決有挑戰性的新問題的潛力。在IT這一行,憑藉以往的經驗乾重復的活是幹不長久的,原因很簡單,一個問題不會被解決兩次,當你發現你的經驗能夠解 決一類問題時,別人早把解決這一類問題的套路編寫成framework,新上手的人即使不具備你的這些經驗也可以呼叫framework中的類和函式來解 決問題,然後在這個framework的基礎上積累新的經驗解決新的問題。正因為如此,全世界開發人員的經驗才會積累起來,促使軟體技術發展得如此迅速。 這並不是說經驗完全沒有用,最關鍵的,學習和思考也是建立在以往經驗的基礎之上的。另一方面,現有的framework並不一定是某方面開發經驗的完美整 合,也需要不斷髮展,用新的辦法重新解決老問題,以Web開發為例,從早期的ASP、PHP到後來的.NET、J2EE,到現在的Ruby on Rails、Django等等,這些framework解決的是同一問題,就是如何快速有效地開發Web應用,這方面的經驗被不斷重新整合,推陳出新。總 結一下,什麼才是核心競爭力呢?應該是在經驗的基礎上學習新技術、解決新問題的能力。

    6、做驅動開發還是做應用開發更有前途? 
    意思就是說,“你告訴我哪個更有前途,我就好好學哪個,另外一個就不用學了”。問這種問題的學員往往會同時問另外一些問題:我以後就想做驅動開發,你教我這些應用開發的技術有什麼用?C++用得多嗎?學了有什麼用?我以後不想做GUI,你教我Qt有什麼用?

    學習最忌諱的就是“有用的就學,沒有用的就不學”這種功利的態度。兩個問題:第一,在你還沒學進去、還不瞭解這種技術時,要如何判斷這種技術學了有沒有 用?只能是根據道聽途說,看各種論壇上都怎麼說的,豈不知論壇上參與這種討論的100%都是菜鳥,有的水平還不如你。第二,就算你學的技術沒有用上,有什 麼損失嗎?從亞嵌畢業的學員從事各種各樣的開發工作,有做驅動的,有做系統程式設計的,有做GUI的,有做Web開發的,只要確定了做一類工作,就不可能把在 亞嵌四個月學的知識都用上,但至少也用得上3/4的知識,假設剩下的1/4你一輩子也沒機會用上了,那也就損失你一個月的學習時間而已,相比於你的收穫, 這算是很大的損失嗎?請注意,上面的假設是不成立的,沒用上的那1/4也只是暫時沒用上而已,程式設計師要換工作或者換專案是很常見的,任何人都不可能只涉及 一類開發工作,只要有紮實的基礎、完備的知識體系,任何工作都能輕鬆上手。

    紮實的基礎,完備的知識體系,我們在安排就業班課程體系的時候,正是以這兩點為依據的。有的課程內容很少有學員在以後工作中會用到,但是缺了這一環就不成 為一個完備的知識體系,例如MMU和Cache,那這種課該不該上呢?毫無疑問該上。有的課程培養一種基本的程式設計思想,例如通過C++來講面向物件程式設計, 通過Qt來講面向物件、事件驅動和狀態機程式設計,這些程式設計思想是程式設計師必備的基本素質,而C++和Qt可能有些學員以後工作用不到,那這種課該不該上呢?毫 無疑問該上。至於還有些人爭論說C++不如Java用得多,Qt不如GTK用得多,請翻回去看FAQ4,這種爭論是無意義的,有工夫爭論誰優誰劣,不如把 兩種都學了,會更有收穫。

    回到做驅動開發還是做應用開發更有前途的問題。我只能說,做好了都有前途,做不好都沒有前途,只會做一樣而完全不懂另一樣是最沒前途的。不要以為核心開發 者就不寫應用程式,Linus寫了一個原始碼管理系統Git來維護核心,因為覺得現有的原始碼管理系統都不好用。牛人都是這樣,需要什麼就寫什麼,才不管 是kernel space還是user space。同樣,做應用開發如果不懂核心,也沒有辦法很好地利用核心提供的服務寫出效能最優的程式。做核心難,因為除錯難,要跟蹤大量的併發執行緒,因為 入門難,要寫一個hello world都需要學很多知識。做應用也難,回頭去看FAQ1,電腦科學從理論到實踐大部分都在上面兩層做文章。所以不存在哪個更難哪個更有前途的問題, 任何關於哪個更難的討論都是too naive的。

    7、我一開始看什麼書都看不懂,怎麼才能理出一個學習順序? 
    以前有個學員在學C語言時說,“C語言很多地方都很奇怪,都得用核心的知識去解釋,可是你又不先教我核心,我沒法學C語言。我只好自己看作業系統的書,看 核心程式碼,可是看不懂。”當然看不懂了,核心程式碼都是用C寫的,如果不學核心就沒法學C語言,那不學C語言又怎麼可能看懂核心?看來這是一個雞生蛋還是蛋 生雞的問題。

    懶真的是人的本性,就連學習的過程都希望是一條路順利地走下去,不用動腦就能學會的:身後走過的路都是“已知”,每走一步就把眼前新的“未知”變成“已 知”,如此一路走來,把所有的“未知”都變成“已知”就算學成了。可惜,知識不是一條路,而是一個圈,你從任何一個地方跳進這個圈開始走,身後都是“未 知”,眼前也都是“未知”。有的人就是不能容忍自己的身後是“未知”:看一本書,一個新的概念A是用我不瞭解的概念B、C來解釋的,我連B、C都不懂怎麼 學A?沒法學了!

    不是人家書寫得不好,而是沒有任何辦法能把一個圈扯成一條直線的。學習的過程本質上就是一個迴圈往復的過程,唯一的辦法就是“存疑”:在本子上記著,有 B、C這樣兩個概念是我暫時不理解的,然後就不再去想這回事,而是相信自己已經理解了B、C,基於自己的理解和假設去學習A,由A再去理解X、Y,這樣學 下去,走完一圈之後再回來,自然就明白當初對B、C的假設正確不正確了,理解了這兩個概念,就從本子上劃掉,這時需要再走一圈,把原來的一些錯誤認識糾正 過來。所以,任何書都要至少看兩遍,第二遍看的時候你會對很多概念有新的認識,因為你看過這個概念後面的章節,在此基礎上產生了新的認識。古人早就明白這 個道理,所以提出了“溫故而知新”。

    亞嵌的課程體系經過多年教學實踐的錘鍊,已經很好地理順了知識之間的關係,使你從最佳的位置跳進這個圈開始學習,所謂“最佳位置”是指,在你初學的時候需 要容忍的“未知”儘可能少,需要做的假設儘可能少,但要想消除所有的“未知”是不可能的,例如沒有學核心就要學C語言。根據你以往的經驗和一些運氣因素, 你對核心的一些假設可能正確也可能錯誤,但這並不影響你學C語言,對核心的一些錯誤假設可能會導致在學C語言的過程中有些誤解,但沒關係,只要跟著我們的 課程體系一步一步走下來,這些誤解和錯誤的假設最終都會糾正過來。

    8、有哪些好書可以推薦一下嗎? 
    能問出這個問題的都是聰明的學員。看書學習是入門過程中非常重要的一環,如果用一本爛書入門,浪費時間還是小事,如果被誤導了就麻煩了,如果形成的錯誤認 識不能及時糾正,變得根深蒂固了就更麻煩了。所以,看書一定要有“品牌意識”,在決定看書學習一門技術時先問問這一領域最權威的書是哪本,這裡列舉一些 Bible級別的書:
    The C Programming Language, 2nd Edition; 
    C++ Primer, 4th Edition; 
    Structure and Interpretation of Computer Programs, 2nd Edition; 
    Introduction to Algorithms, 2nd Edition; 
    Compilers: Principles, Techniques, and Tools; 
    Advanced Programming in the UNIX Environment, 2nd Edition; 
    TCP/IP Illustrated, Volume 1: The Protocols; 
    UNIX Network Programming Volume 1, 3rd Edition: The Sockets Networking API; 
    Understanding the Linux Kernel, 3rd Edition; 
    Linux Device Drivers, 3rd Edition。

    在學習過程中,眼界一定要開闊,不要學到一點東西就沾沾自喜,坐井觀天,以為這就是技術的全部。要多和別人交流,多瞭解別人在看什麼書、別人對技術的認識是怎樣的。書是看不完的,活到老學到老,對於程式設計師來說尤其如此。

    9、如何處理打遊戲和學習的關係? 
    ESR的文章中也說過,黑客們都有一些特別的業餘愛好,並且往往是非理性思維的愛好,大概是需要換換腦子吧。典型的例子是因滑翔機事故去世的 Stevens,他寫了FAQ8所推薦的Bible當中的三本。愛打遊戲的優秀程式設計師肯定大有人在,有一些還成了優秀的遊戲開發人員。但是我認為,要想成 為優秀的程式設計師,必須有兩點基本素質,一是對程式設計非常感興趣,二是對所有別的事情都不感興趣,或者都不如對程式設計感興趣。有人說,人的一天有24小時,8小 時休息,8小時上班,另外的8小時在幹什麼就決定了你以後有多大發展。一有時間就看書學習,這就是一個程式設計師應該做的。今天打遊戲,明天看電影,後天炒 股,那麼你就等著三十五歲下崗。如果你不能夠認同程式設計和思考是比其它事情更有吸引力的,那麼你不必學程式設計,集中精力去做最喜歡的事情或許會有更大的成就。