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

評論
評論

本文作者 Arthur Liao,為 Twincl 網站、途聞國際創辦人、法輪大法修煉者 ;原文刊登於 Twincl 網站 ,Inside 獲授權刊登。

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

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

一、我適合當程式員嗎?

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

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

一點天份

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

當程式員不一定要有多高天份,畢竟像 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」。

寫出好的代碼並不容易。假如我們從 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 角色。

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

結語

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

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


精選熱門好工作

Campaign Manager 活動企劃經理

樂購蝦皮股份有限公司
臺北市.台灣

獎勵 NT$20,000

客戶規劃顧問 Customer Success Associate

SHOPLINE 商線科技有限公司
臺北市.台灣

獎勵 NT$20,000

DBA資料庫管理師

酷遊天國際旅行社股份有限公司
臺北市.台灣

獎勵 NT$20,000

評論