7-0
1條留言

程式員求生指南:關於寫程式的二三事

我是一個熱愛寫程式的傢伙。我的第一台電腦,是13歲時買的Apple II,在那之前,我已經開始到同學家用「小教授二號」學寫程式了。高中時我當電腦社社長,帶隊參加教育部辦的全國程式大賽,幸運拿到冠軍,大學、研究所唸的也是相關科系(台大資工/Stanford CS)。工作20年來,一直從事軟體相關領域,即使擔任主管職務,也一直對技術充滿熱情。

寫程式寫了這麼多年,多少有些體會。我把自己對寫程式這份工作的心得寫下來,希望能給從事相關領域或有志於寫程式的人參考。


Photo credit: gags9999@flickr CC BY 2.0

一、我適合當程式員嗎?

程式員,也叫軟體工程師、程式設計師,對岸叫軟件工程師、程序員。我覺得「程式員」三個字簡潔有力,所以就用這個詞。

如果你正從事這份工作,恭禧你!這是個熱門行業,在可預見的將來,也不會消失。不過也別高興太早,這一行的技術汰舊換新非常快,必須不斷努力學習才行。

一點天份

打開一個空白檔案,必須創造出程式。與所有創造性的工作一樣,寫程式需要某種程度的天份。程式員生產力好壞差別很大,倒不是說一天能寫多少行程式(這可能是最沒參考價值的數字了),而是品質有天壤之別。天份很高的程式員,一個抵十個,沒天份又不努力的,一天製造的問題可能多於解決的問題,生產力是負的。具體來說,邏輯推理、抽象思考、創造力、理解力,這些都是相關能力。

當程式員不一定要有多高天份,畢竟像Linus Torvalds(Linux創始者)那樣的天才很罕見,但一點天份還是必需的。如果你發現自己寫程式、看程式、解bug都很痛苦,半年一年了也不見改善,也許這份工作不太適合你。

一些熱情

如果你對寫程式充滿熱情,又有一定的天份,那再好不過。最起碼,你有時會沉浸在寫程式或解bug的情境中(英文有個詞叫“flow”,心流)、不想被中斷,這樣就夠了。如果你從未出現過這種情境,那麼你可能不會熱愛這份工作。不過沒關係,世界上不熱愛自己工作的人其實不少。如果你能做好這份工作,眼前又沒有更好的選擇,繼續做下去也沒問題。

很多努力

努力是一定要的。當一名好的程式員,要學習的東西太多了,而且不努力很快就會被淘汰(雖然很多工作都是這樣),這是入這行前應該要有的體認。

二、程式員基本能力

職業道德

甚麼?寫程式也有職業道德?有的,而且還很重要。我說寫程式是一門良心事業,因為通常你寫的程式只要符合規格、能正確執行,就可以交差了,而你的主管或同事很難一眼看出程式碼品質有問題,例如:在特定條件下會爆掉、濫用複製貼上、採用一些骯髒寫法、程式可讀性很差、模組之間糾結在一起,等等。

你焊接過電路板嗎?要是電路板繞線一團亂、零件歪七扭八、接腳沒焊好,你能交差嗎?但是寫程式可以。因為程式碼是一種抽象產品,沒有「外觀」可以觀察。如果你的團隊要求code review,這個問題可以得到某種程度的改善,但仍不能徹底解決。程式員的紀律和職業道德很重要。(關於軟體的抽象本質及軟體開發的特殊性,我去年寫過一篇英文blog探討這一主題,有興趣的人可以參考看看:“Making Abstract Products Makes Things Hard”

程式語言

程式語言的學習,是程式員最最基本的能力,而且應該至少精通一兩種語言。隨著程式經驗的累積,學習不同程式語言的速度會越來越快,例如從幾個月縮短到幾週。當然精通一門程式語言,不是幾星期、甚至幾個月就能達成的,但迅速接手並維護既有程式碼,是對合格程式員的基本要求。

通常第一種程式語言學最久,因為很多觀念也是第一次學,例如變數、迴圈、陣列、遞迴、I/O、網路、多執行緒、物件導向、regular expression、functional programming……。等到學第二種、第三種程式語言,新的觀念越來越少,主要在學語言本身,速度就會變快很多。

資料結構及演算法

如果你是本科系畢業,資料結構及演算法應該是必修課。如果沒學過,建議花點時間學一下。倒不需要買一本厚厚的書折磨自己,但基本的概念一定要有,例如:

  • 資料結構
    • 陣列(array)、串列(list)、堆疊(stack)、佇列(queue)
    • 樹(tree)、二元樹(binary tree)、雜湊表(hash table)
    • 指標(pointer):這也許不算資料結構,許多高階語言也不讓你用pointer,但是對記憶體、指標要有概念,這是程式員與非程式員的區別之一
  • 演算法
    • 對以上資料結構的各項操作
    • 排序(sort):至少搞懂3、4種基本的排序演算法,例如bubble sort、quick sort、merge sort等
    • 搜尋(search):depth-first-search、breadth-first-search、binary search等
    • 其它:迭代(iteration)、遞迴(recursive)、分治法(divide and conquer)、時間/空間複雜度的基本概念(big O)等

網路上資源很多,Google一下、多寫一些程式練習,弄懂以上基本概念,應該就夠用了。

網路協定

TCP/IP、HTTP、DNS等這些都是基本的網路協定。不需要到專家程度,但身為一個程式員,除非你的工作與網路完全無關(這種工作應該越來越少了),否則對這些網路協定的運作應該要有起碼的了解。例如你能講清楚,從你在瀏覽器輸入一行網址到看到網頁內容,在網路上發生了哪些事?以前我在Yahoo面試前端工程師的時候,喜歡問一個問題:請解釋cookie是怎麼運作的,結果不少人答不出來。

當然現在的程式開發環境很方便了,各種library一大堆,我們通常不需要自己實做這些底層的東西。但不懂這些東西運作的基本原理,會讓你在debug時被卡住,因為整個網路系統的運行,都是建立在這些基礎架構之上。這些網路協定,再過很多年還是會繼續存在,花一點時間搞懂這些,我認為很值得。

除錯能力

講除錯能力不太準確,因為除錯不是單一能力,而是結合了經驗、對程式的了解、對系統架構的了解、抽絲剝繭的能力、直覺,以及各種hands-on能力的綜合,就像當偵探一樣。台語有句話叫「醫生怕治咳,師傅怕抓漏」,差不多就是這個意思。

我在Yahoo工作期間,最刺激的事莫過於排全球on-call了。所謂on-call,就是全球Yahoo網站出包時,你要在最短時間內找出問題並修復,那真是超級debug。拜託,Yahoo網站那麼複雜、程式碼又那麼多,出問題的模組又不是我寫的,美國同事都下班了,誰知道怎麼解決?對不起,那是你家的事,排了on-call你就得想辦法解決。功能上的問題還有跡可尋,最棘手的是像系統過載這類問題,爬log、寫script、trial-and-error,總之想方設法揪出元兇。

程式員應該具備良好的除錯能力,不管程式誰寫的。另外,修bug也是一門學問,是採用鋸箭法、貼狗皮膏藥,還是找到病灶、解決問題背後的問題,就看程式員功力了。

寫出可讀、易維護的代碼

這個要求聽起來很合理,不是嗎?其實這是最難的。寫程式這麼多年,看過多少代碼,我跟你說,這個世界上的爛code佔絕大多數,好code只佔一小小部份。我自己也不斷在這條路上努力著,到現在也不敢說自己寫的code多好。

為甚麼這件事這麼難?我想了一下,大概有以下幾個原因:

  1. 可讀性高的代碼,通常是用很好的解法,解決了真正的問題

    • 你需要徹底了解問題(problem domain)
    • 你需要考慮過至少幾種合理的解法(solution domain)
    • 你需要對程式語言、程式庫、既有程式架構和可運用工具很嫻熟
    • 你要能以簡馭繁,而這代表你掌握了更高的東西
      • 例如牛頓的F=ma、愛因斯坦的E=mc2,這是神人等級的功力(只是舉例說明,寫程式不需要到這樣)
  2. 你寫程式必須很有紀律,例如:

    • 不急著馬上寫code,先想清楚問題、解法、架構
    • 恰到好處的註解,少了不行、過猶不及
    • 用心想過的命名(程式界有句名言: There are only two hard things in Computer Science: cache invalidation and naming things.)
    • 壓抑copy & paste及產出一堆爛code的衝動:
      • Bingo! 找到一段code了,看我的copy & paste
      • 可以work就很好啦、這麼漂亮的解法……
      • 好累,我不行了,先commit code再說
    • 在commit code之前,自己再好好review一次(我個人經驗,通常這個步驟可以改進程式好幾個地方)
  3. 越容易維護、擴展的代碼,代表它的複雜度越低

    • 降低軟體複雜度是軟體工程的最大挑戰,軟體複雜度就像軟體的熵(我的第一篇英文blog “Software complexity is software entropy”,就是講這件事)
    • 必須做到low coupling、high cohesion,而這兩件事都很難
    • 低複雜度的軟體系統,代表裡面各個模組的複雜度都必須更低
      • 當你輕易多用了一個外部組件、增加了一個external dependency,你就把它的複雜度整個帶進來了,所以要很小心
  4. 現實環境的因素,導致好的程式碼不易產生

    • 專案時程的壓力
    • 程式員經驗的限制
    • 團隊未採用一些最佳實務
    • 有決策權的人對軟體開發不夠了解

代碼品質的重要性,每個人都知道,連路人甲都知道。現實的難處在於:第一版的代碼,只要能work,品質好壞是很難看出來的;它們的差別,要到系統後續的運行、維護及擴展才能看出來,然而此時木已成舟,程式只能修修補補繼續用下去,最多小幅重構(refactor),直到軟體生命週期的結束。

寫出好的代碼,時間會花比較久、會導致專案時程延後嗎?其實並不會,這是能力限定,不是時間限定。寫出第一個版本,花的時間都差不多。但後續版本就差很多了,寫得越好的代碼越好改。你如果改過那種high coupling的系統,你就知道我說的意思了,那真是人仰馬翻,超high的。這種代碼要是裝在箱子裡,箱子上會標示「易碎/FRAGILE」。


Photo credit: Hsing Wei@flickr CC BY 2.0

寫出好的代碼並不容易。假如我們從1分到10分給程式碼打分數,10分真的很難很難,我自己也做不到。但一般人經過努力,達到6、7分應該是沒問題的。如果你想看書,我在這裡推薦一本:Code Complete 2nd Edition。教人寫程式的書中,這是我看過最好的一本了,只是內容比較多,需要時間消化。如果還有興趣多看,我個人覺得Martin Fowler也寫了不少好書。

三、程式員進階能力

具備以上的程式員基本能力,我想就足以勝任大部份「單兵程式員」的工作了。如果想在技術上更上一層樓,以下是幾個我認為比較重要的進階能力,提供給大家參考。

作業系統

大學修的那麼多課裡面,我感覺對工作最有用的就是「作業系統」這門課了。對作業系統(OS,operating system)的了解,是資深程式員應該具備的。例如:

  • Hardware: CPU, memory, I/O devices
  • Process, multi-thread, scheduling
  • Inter-process communication: signal, socket, pipe, named pipe, shared memory, message queue...
  • Synchronization, deadlock, mutex, semaphore
  • File system, cache, virtual memory, page fault...
  • Real-time system, distributed system

作業系統本身就是一支超大型程式,有著無數前人的心血。加上作業系統的基本概念,幾十年不變,所以花點時間弄清楚這些觀念,我認為很值得。

資料庫

不是每個程式員的工作都會使用到資料庫,而且現在不少人用NoSQL存資料。儘管如此,我認為關連式資料庫(relational database)還是很重要,不管是MySQL、PostgreSQL、MS SQL或Oracle都好,資深程式員應該至少對其中一種有相當的了解。

題外話,多年程式寫下來,我對ORM(object-relational mapping)抱著存疑的態度。網路上有篇文章:Object-Relational Mapping is the Vietnam of Computer Science,應該是反ORM的代表作之一,有興趣的人可以看看。還有一篇有名的文章:The Law of Leaky Abstractions,講的是每一層抽象化都或多或少會有漏洞。從leaky abstraction角度來看,SQL已經是一層有洞的abstraction了,而ORM洞更大!(註:寫這兩篇文章的兩個人,剛好就是Stack Overflow的兩位創辦人,真巧。)

網路安全

網路安全(network security)平時很容易被忽略,因為它費事費工,沒有立即效益。但是對網路安全的輕忽,一旦出事,經常導致企業或政府重大損失。這讓我想起以前當社區管委會主委的時候,按消防法規要搞甚麼社區消防編組、演訓,還要指派防火管理人,真的很麻煩。安全這種事情就是這樣。

有些網路安全議題,是屬於系統管理者的範疇,例如DoS (denial of service)、DNS spoofing、man in the middle;有些則是程式員的責任區,例如SQL injection、cross-site scripting、cross-site request forgery等等。此外像驗證使用者身份的流程、儲存/傳送使用者敏感資料的方式,也都與安全有關。資深程式員對網路安全議題及常見攻擊手法,應該要有足夠的認識與敏感度,並在開發過程中合理採取預防措施。

程式語言的多樣性

程式語言是程式員吃飯的傢伙,除了每天工作上用到的,資深程式員也應該接觸一些不同的程式語言。例如:

  • 函數程式語言

    函數程式語言(functional programming language)是另一種風格的程式語言,可以挑一個好好學一下。我個人推薦Haskell,但F#、Scala、OCaml、LISP、R、Erlang、Clojure這些也都不錯,各有擁護者。

    實際工作上,不見得有機會使用這些函數程式語言,但好好學一種,可以拓寬自己程式設計的思路。而且現在很多程式語言,包括C++(C++ 11之後)、C#、Java(Java 8之後)、JavaScript、Python、Ruby、Swift等等,都具備一定的functional programming能力,可以運用在工作上。

  • 組合語言

    除非是用C加assembly寫硬體相關或compiler/toolchain的人,組合語言在實際工作中很少用到。但我覺得應該了解一下,因為這是軟體的最底層,再往下就是硬體了。我中學時候寫過6502、8088,大學上過一堂MIPS組合語言的課,其實還蠻有趣的。寫過組合語言,會讓你對電腦如何執行程式更有「感覺」。

    但是組合語言不用太認真學,因為真的很少用。學個概念、最多寫幾個小練習即可。

  • Shell Script

    如果你工作中有用到Linux/Unix相關的OS,我建議應該要學一種shell script,例如bash。如果你是ops/service engineer或系統管理者,這應該是必備能力了,不過資深程式員最好也能懂這些。就像vi一樣,有些東西已經很古老了,但網路世界就這麼運作著。沒辦法在terminal環境工作的人,很多問題處理起來就顯得笨手笨腳。

四、非關技術

除了專業技術能力,我再補充一些非關技術的心得。

克制砍掉重練的衝動

在開發過程中,程式員很容易對既有程式碼產生一種「這誰寫的?砍掉重練比較快」的衝動,包括我自己在內。我想可能的原因有:

  • 砍掉重練其實比較容易(拆掉舊屋蓋新屋很快,保留這面牆、那扇窗的反而更困難)
  • 在自己的地盤當山大王很開心(人都喜歡按照自己的意思來)
  • 在系統發展的過程中,很多需求後來才出現,使當初的架構顯得捉襟見肘,但在當時其實是很合理的設計
  • 上線多年來,程式員處理了很多狀況、修復很多bug,因此程式顯得沒那麼乾淨優雅
  • 寫程式比讀程式容易
  • 文人相輕
  • (排除以上各種因素之後)當初的程式碼真的寫很爛

不管怎樣,砍掉重練(rewrite)的代價,通常比乍看之下高許多,而且日後維護你的程式碼的人,心裡可能同樣嘀咕「這誰寫的,砍掉重練比較快」。Joel Spolsky在2000年寫的一篇 “Things You Should Never Do, Part I”,今日讀來依然犀利。

小幅重構(refactor)是沒問題的,而且可以經常做。

理解不同人的立場

當我們在某一方面懂得比別人多時,容易產生一種傲慢,技術人員也是。在專案開發的過程中,除了技術團隊,還有產品/專案經理、主管、客戶、使用者等不同角色的人介入。在技術方面懂得比別人多,並不妨礙我們理解他人的立場。當我們能站在別人的角度看問題,常常一下子就能了解為甚麼事情會這樣。例如:需求改來改去、一開始不講清楚、進度卡在別的team上面、「請你用最快方式完成」、「先支援這件緊急要求」、沒人把我講的話當一回事……

做到理解他人或是同理心(empathy),其實並不容易,因為每個人都有自己的立場,而人們傾向站在自己的立場看問題。我費了很大功夫,一直在努力修正自己技術傲慢的心態。如果你技術很厲害,又能做到理解別人,那真的很不簡單,你所在的團隊運作一定更為順暢。

參與社群,吸收新知,寫點東西

不管公司大小,資深程式員若只把觸角侷限在公司內部,會越來越封閉。接觸外面的社群、吸收專業領域的新知、寫點東西累積自己的專業credit,會讓自己成長得更快。現在參與社群的管道很多了,從專業聚會研討、開源碼專案到各種社團,五花八門,不過還是得衡量一下自己能投入的時間。單身的人比較有空,有家庭有小孩就只能斟酌參與了。

吸收新知是為了讓自己保持在敏銳狀態,不要變成滅絕的恐龍,但也不用太過焦慮。軟體領域變化快速,各種語言、框架、技術不斷冒出來,要追新知永遠追不完。如果你時間充裕,可以到處追新知,那很好。若你時間有限,我的經驗是:等到很多人都在談論的時候再去了解一下,也就夠了。跟工作有關的,根據自己在團隊中的角色,適度學習即可。

另外,我建議有幾年工作經驗的程式員,都應該考慮寫點技術文章,累積自己的專業credit。這種事情沒有看起來那麼可怕,一回生二回熟,包括找主題、寫文章,多試幾次就會上手。也不用給自己太大壓力,一、兩個月寫一篇都可以,長短不拘,日積月累,會有收穫的。(插播一下:如果不想花力氣架部落格,歡迎來Twincl網站發表文章;這裡還有我獨家開發的「所見即所得」Markdown Editor,寫文章蠻方便。)

程式員之後

寫程式能夠寫幾年?每個人情況不同,但無論如何,大多數人不會寫一輩子。當了單兵程式員若干時日之後,最常見的角色轉換就是先成為Tech Lead帶組員(不同公司對這個角色有不同安排方式),此時除了寫程式,還要負責帶團隊、對外溝通、掌控時程、照顧組員、處理突發狀況等等。如果公司夠大,公司可能還會提供更多資深技術職位,例如Architect角色。

技術職之外,有的人會走管理職,有的人走專案管理或產品經理,甚至業務、行銷都有;如果喜歡走技術,有的人會跳槽到條件更好、發揮空間更大的公司,其實選擇不少。如果有心創業或加入創業團隊,紮實的技術底子也會令你如虎添翼。

結語

最近學程式設計忽然變得很流行,「一小時學程式」、小學生學程式,好像程式人人都能寫似的。當然練習寫幾行小程式是沒問題,透過這些訓練邏輯能力也很好,但真實世界的程式,複雜度遠遠超出這些沙盒小練習。事實上,隨著電腦及網路技術的發展,現在的軟體開發比起一、二十年前更複雜了,有時我都很佩服現在剛出校門的學弟妹們,要學的東西這麼多,他們是怎麼辦到的。

洋洋灑灑寫了一大篇,其實很多也只能點到為止。希望這篇文章,對大家有一點幫助!

15筆討論 回應文章
顏御軒5年

感謝版大您的文章,看完這篇文章讓小弟發覺現階段的自己尚有許多待改進之處 希望未來能朝著您所說的這些特質成為好的工程師 與各位分享一下小弟謹記的名言:「改變不需要馬上,而是每天進步一點點!」 共勉之 :)

謝謝你分享的名言!我以前有一個小小的心得:大部份人都是到了不得不改變的時候,才會改變自己。能主動改變自己,並且持之以恆,你的努力一定不會白費。加油!

看到這篇文章真的勾起了很多記憶,組合語言寫程式攔截 INT13....那時候挺好玩的!版大您的文章真的寫的太好了,點出了很多的觀念,謝謝您的分享。

Arthur Liao5年(更新)

你這麼一說,我又想起了更多事情。以前在Apple II寫6502,我覺得最好玩的是戳喇叭讓它發出長短不同頻率的聲音。奇怪,我以前怎麼有那麼多閒功夫。

在說OS的部份,應該是"Real"-time system,不是"Read"-time system。 這篇文章寫的很不錯 :3

已更正,謝謝!

很多觀念其實都一而再的聽過了,但是自己實際執行起來,難度很大,現實很殘酷。

Arthur Liao5年(更新)

我完全理解。很多寫程式的人心中都有個理想境界,但既有程式碼的現實、職場環境對待軟體開發的心態,常使自己不得不先放下「理想」。我想,環境是人造成的,在提升自己專業能力的同時,加上大家的努力,也會使環境發生變化。大家一起加油!(希望對台灣軟體環境能有一點貢獻,也是我離開Yahoo自行創業的原因之一,當然還有一段長路要走就是了。)

寫得很好!「職業道德」真的十分重要。 如果是用強類型語言編寫的項目,理論上應該可以描繪出它的「電路圖」,這對檢視程式的質素應該十分有幫助。

如果是用強類型語言編寫的項目,理論上應該可以描繪出它的「電路圖」,這對檢視程式的質素應該十分有幫助。

確實,強類型語言有很多靜態分析工具可以運用,這在大型專案上很有利。比如Java在企業軟體領域大量被使用,我認為與它適合大型團隊生產線的特質有關:程式要先編譯,分析工具多,自由度低、程式碼品質的發散幅度比較小。不像scripting language(PHP、JavaScript……),自由度高、程式碼品質的發散幅度超大,很多bug都是在runtime/production系統才會發現,必須仰賴很多unit test及test automation補強。

Hello Arthur,   我們是一家香港的初創公司,打算到台灣設立IT開發團隊,請問在那裡可以找到優秀的程式員呢?我們深信在薪金、前景和公司文化各方面都非常吸引,感謝推薦!

這算是求才的問題吧?在我過去的工作經驗裡面,求才是重點任務之一。比起香港,台灣確實擁有更好的軟體人才庫。歡迎你們到台灣設立開發團隊!:-)

你們已經在台灣辦好公司登記了嗎?這事沒辦好,是沒辦法在台灣正式開始找人的。(以前我在NetBase工作的時候,是我幫他們在台灣辦公司登記,一開始我先遠端在台灣工作,不過這算是特例。)

我建議以下幾個管道,同時進行:

  • 在104人力銀行開職缺
  • 利用社群媒體宣傳(例如FB社團或PTT)
  • 找獵人頭公司

有問題歡迎提出來,這方面我算很有經驗了,可以提供一些心得給你們參考。(不過請另開一篇,不然樓就歪了。)

學了10年程式,一覺起來,搞不好就被淘汰了~

補充我昨天在某個臉書社團關於「前端vs後端」的討論回應,內容如下:

以下回應比較長,所以我重新貼一段文。

前端的code不會比較難寫,但是經常比後端的code難維護。我舉一個幾年前在Yahoo工作時碰到的維護CSS的例子。當時Yahoo網站的前端程式架構,為了讓各個國家及property(property指的是新聞、運動、娛樂等不同網站)可以有自己的版面風格,所以各國家及property都可以有自己的CSS overwrite,這些CSS overwrite再按照一定的順序載入。你知道CSS長到幾千行的時候,就會變得很難讀懂,而不同國家及property的工程師為了達成PM/designer的要求,有時會為達目的不擇手段,所以經常可以看到「!important」出現在CSS原始碼裡,因為第一次用「!important」的人可以不用管整團CSS是怎麼攪和在一起的。如果你是在下游,用這種X招是可以work,但在中上游的人就慘了。我發現只要我動一下CSS,下游property的網頁就壞了,要改就必須把那一堆「!important」和各種奇奇怪怪的CSS overwrite rule都理順修好。有些我勉強改完了,有些實在沒辦法,只能跟PM和designer說抱歉。

前端開發是個叢林世界,光是一個CSS的coding style就很難統一,何況還有前端的各種不同framework、design pattern(或no pattern)及開發風格。不同的工程師有不同做法,簡直是八仙過海,各顯神通。我覺得前端的code要寫到可讀、易維護,比起後端更難。前端的工程師們,大家辛苦了!(此時腦海浮現「啊,福氣啦!」及周潤發廣告的畫面)

太好了 終於看到有一篇 資深的程式(猿) 寫給剛入門的一些道路指引(2013年我能夠看到就好了)