軟體工程師(續) - New Grad 書單

學校畢業後,通過面試進入業界。我發現僅僅掌握好學校教的東西是不夠用的。身為軟體工程師,我覺得各方面能力我都不足。從同事們給我的回饋中,我選擇了幾本書和資源來培養軟體工程師的基本能力。

  • Clean Coder
  • Clean Code
  • The Unix Programming Environment
  • Design Pattern
  • Building Microservices: Designing Fine-Grained Systems
  • Top Signs of an Inexperienced Programmer

Clean Coder

Clean Coder Amazon link

這本書告訴我專業是怎麼一回事。今天公司雇用我當軟體工程師,公司需要的是我的專業,不是讓我來製造麻煩,也不是讓我來承受不住壓力感受崩潰。身為專業的人,我得

  • 有如同日本職人一般的心持續不斷精進軟體工程知識和技術
  • 知道怎麼在緊迫的時間線下讓專案順利進行
  • 學會處理自己與同事與上司的衝突
  • 清楚該如何向主管承諾完成工作的時間點
  • 學會快速進入工作模式和放鬆
  • 會說No,以及怎麼說
  • 會說Yes,以及清楚知道這代表什麼含義

這本書雖然是與寫給軟體工程師,可是裡頭講的概念我相信也能應用在其他領域。它帶給我一個方向。這個方向引導我理解一個人進入職場後該有的工作態度和方法。書中有許多例子。其中,這一個讓我覺得相當深刻。

當主管A跟工程師B說,想要這週五讓B完成某個專案。B評估覺得不可能,因為B手中現有的任務複雜程度讓他沒有餘裕去完成新的任務。B向A說No,也表明手中已經沒有時間,如果A想讓他做新的任務,那麼之前承諾會完成的任務勢必推延到下下週。A不開心B的回答,A覺得B像是在找藉口。A說他已經和自己的主管C承諾接下這個新任務,這週五得完成,仍然希望B再撥出一點時間幫忙。B心裡知道A的難處,可是B也知道如果再接這個新任務他一定會身心俱疲然後崩潰,而恢復的時間會超過兩個禮拜,這兩個禮拜B沒辦法做任何事。B清楚知道自己身心極限,以及崩潰的代價。B是一個專業的工程師,他再次向A表明這不可行。B也說如果接下任務,他沒辦法保證品質,萬一不夠好,AB兩人都會受到責難甚至開除。B要A向C重新安排新任務的死線。A最後失望的接受。

這是一個不舒服過程,然而B做的是對的。B展現專業的一面,他清楚知道AB兩人都無法承受接下任務的風險。B沒有盲目接受A的要求,也因此帶著兩人避開危機。這個例子中,A反倒是不專業的一方。A在不清楚B目前可以承受多少任務,也不清楚新任務複雜度的狀況下直接向主管C承諾這週五完成。A的行為讓他與B暴露在任務失敗的風險中,非常不明智。

Clean Coder 是一本解釋何謂專業以及該怎麼做的書。我推薦所有在職場中感到莫名壓力和無所適從的人閱讀。它可以帶給你一個方向去培養自己的專業來應對來自工作上的困境。

軟體工程師是一個需要與人互動的職業。這是書中另一個讓我印象深刻的觀點。大部分的人認為軟體工程師只需要寫程式碼和培養所謂的 techincal skills 就行,可是那些僅僅是基本功或者說那些身為軟體工程師的基礎。在那之上,軟體工程師還需要懂得與人交流和溝通才能順利完成工作並展現專業。

Clean Code

Clean Code Amazon link

一本令人不意外,卻又意外的書。這本書很多人推崇,也算是軟體工程領域中接近聖經等級的著作。作者 Robert Martin 從自身經驗中歸納出一套整潔程式碼的標準,同時也探討程式碼為何變得複雜以及該如何化簡,最後建議使用 test-driven development (TDD)。我很意外我推薦了這本書,我還在 USC 唸書時僅僅看簡介我會以為這些都是熟悉不過的常識,直到我上班時盯著組裡的 legacy code 懷疑人生……

Clean Code 的精神是寫出人看得懂的程式碼。我認為最重要的就是命名,任何東西的命名包括 class, interface, method, function, variable, enum, ……。首先,一個名字要能夠表現那個東西做的事。我們得盡量避免泛用性的名稱像是 XxxManager 和 YyyHelper,因為那些名稱能夠包含太多東西。沒有定下一個明確的名字,便不會有一個標準決定什麼功能可以加入一個 class,什麼功能要額外開一個新的。XxxManager class 最後就變成一個 god class/object 做了所有的事。再來,我看過有人喜歡使用縮寫,有些是約定俗成可以接受,有些卻是造成閱讀上困擾。例如說,context 有些人喜歡寫成 ctx 還算可以接受,而我看過 task 簡化成 tsk 讓我實在不知道用意何在。最後,如果你需要用很多字彙來命名一個 class 或 method,通常那個 class 或 method 就可以被分割成更簡單的架構。這同時也呼應了 single responsibility principle。

另一個我以前沒注意到的觀念是如何使用 comment。我同意 Martin 的看法。Comment 適合寫動機而非解釋程式碼。我們希望讓程式碼容易閱讀,那我們就使用適當的命名,而非用 comment 解釋一堆和程式碼重複的東西。多餘 comment 的就譬如下面那張圖。
This is a stop sign

另一個有趣的概念是 TDD。按照TDD的方法,我們首先要寫測試,然後再回過頭來寫商業邏輯。我嘗試過使用這樣的方式開發,可是除了unit test 之外,其他較為全面的測試像是 integration test 和 end-to-end test 很難。Unit test 做起來簡單是因為,測試範圍小、執行速度快、不太仰賴 package 外的資源;end-to-end test 則需要與客戶協調出需求才能由外而內逐一寫出合理的測試邏輯。End-to-end test 建構時間長、執行時間長、需要的資源也多。除非所有設計環節已經結束且不再改變,否則過早寫測試會阻礙系統開發的時間線。很多時候,一個設計改變會直接廢棄之前花費心力建構的所有 end-to-end tests。

至於什麼時候該開始寫 end-to-end 和 integration tests,我的大致看法如下:

  1. 系統架構清晰前不要動
  2. 系統infrastructure決定後,開始撰寫 testing library
  3. 建service的時候,同時寫client
  4. 寫商業邏輯,同時建好 testing framework
  5. 完成商業邏輯後,寫 integration tests
  6. 部署產品上 preprod 環境跑 integration tests
  7. 寫 end-to-end tests
  8. 部署產品上 prod 環境跑 end-to-end tests

Clean Code 讀完後我覺得並沒有那麼容易應用在我的工作上。前半段關於程式碼我蠻贊同的,可是不是每個人都會花心力在可讀性上。尤其當我看到 legacy code 之後,腦海裡只有「算了」兩個字。寧願將新產出的程式碼簡潔就好,舊的程式碼如果沒問題就 Let it be 吧!後半段關於 TDD 實際操作起來很不順手。我想他的方法可能比較適用小規模的測試,大範圍的 end-to-end test 其複雜度不亞於產品本身。

Ps. 關於 legacy code,如果可以盡量別去碰,因為這件事帶來的好處兩三年內看不見。依照員工流動率,兩到三年換一家公司,機會成本太高。這件事是寫在履歷上也很難量化帶來的影響和價值。修改 legacy code 有另一本書叫 Work Effiectively with Legacy Code 專門講這件事。作者 Michael Feathers 提出的方法似乎可行,不過我猜很少有人願意去執行。

The Unix Programming Environment

The Unix Programming Environment

這是一本關於使用小黑窗的書。我剛開始工作時,對終端機的熟悉度很低。我大約只碰過 cp, mv, rm 之類的 file system 指令,而是一些其他類別的指令像是 sed, awk, chmod, 等等並不熟悉,也不太懂怎麼使用 grep。這本書介紹了 unix 指令構造以及常用的幾大類指令包含 file system, filter, standard I/O, system call。當然書中也介紹了 shell programming 是如何運作。這些知識幫助我建立一個清晰的架構使用 Unix 系統,懂得查閱指令,懂得寫 shell scripts。

儘管我只讀了一半,卻也夠用了。後半部分的內容目前工作上比較少聽過,可以留到以後再閱讀。有趣的是,關於 shell script 愈來愈多部分可以被 python script 取代。如果是一份簡單的 script 或是不需要依賴額外的 package libraries,我可以直接寫 shell script。反之,如果邏輯略為複雜那直接用 python 會是比較好的選擇。

了解 Unix 系統最大的好處在於我們對程式或服務怎麼被系統執行有一個深刻的了解。我們大部分都是用較高端的語言像是 Java,Python,C++ 撰寫程式碼然後包成可執行的檔案。當程式被執行時就會需要 shell programming 讓 Unix 系統啟動他們。在我眼裡是 shell programming 是重要的中間橋樑,沒辦法被忽略。

Design Pattern

Christopher Okhravi’s Design Patterns Episodes
Head First Design Patterns
Gang of Four’s Design Patterns

Design Pattern 是物件導向的延伸。幾乎所有語言都有 class 和 interface 的概念,而 Design Pattern 就是這兩種概念的應用。Design Pattern 幫助我們依照我們的需求寫出易於重複使用的程式碼,也使得寫好的程式碼易於擴充。

學生的時候或是實習專案,我們很常因為時間壓力產出 fast and dirty 的成品。幸好那些成品的生命週期都會在某個時間點結束,我們不用去擔心是否要維護或是擴充。不幸的是,也因此我們不太在乎程式碼的品質。

Design Pattern 什麼時候用?通常我們開啟一項新的專案,並且從零開始且程式碼的時候,Design Pattern 的重要性就會顯現。身為造輪子的人,我們得幫之後的人設想該怎麼理解專案,之後該怎麼擴充,還有保持他乾淨和可讀性。這個時期就像是大樓蓋房子一樣,需要規劃。一旦有了良好的設計,通常這個專案和成果可以使用很久,有軟體工程知識的人也會懂得怎麼維護它。Design Pattern 從另一個角度看也可以是軟體工程的共同知識,軟體工程師們時常用它來交流。

雖然 Design Pattern 是好的工具,但是也別走火入魔。不是所有的設計一定要摻入 Design Pattern。設計和寫程式的時候心中有它,看到合理的使用情境,思考一下它是否能帶給程式碼簡化、重複利用、可擴充、易讀······等等的優勢,不行的話也別強求。有的時候可能簡單的資料結構就能完成的事,就別讓 Design Pattern 操心了。

Building Microservices: Designing Fine-Grained Systems

Building Microservices: Designing Fine-Grained Systems

Microservice的概念似乎愈來愈基本,可是那時候的我沒有想過這是什麼。基礎的 monolithic service 都還不了解的狀況下碰上這個,有如遇上大魔王。Microservice 是因應 monolithic 擴展性上的缺點而帶出的解答,其精神是將service繁忙的部分分離出來擴展。例如,一個service需要頻繁讀取資料庫,同時卻只需要每天備份一次資料庫。這個情況,我們會將兩者的功能分開成兩個 service。如此,當我們對前者功能擴充時,後者可以保持原樣。這樣的設計讓資源利用變得有效率也不容易浪費。

Building Microservice 這本書就是介紹我們該怎麼了解這樣的系統。當 service 多起來後,service 間如何溝通,如何做到 load balance,如何 deploy,如何測試,如何擴展,如何監控,如何DevOps……。

Microservice的概念以不同的形式被廣泛運用在業界。如果像我之前一樣對它不熟悉,而且手邊有一個 Mircoservice的實例,我會推薦這本書。它可以幫助你從實例中看見對應的概念。

Top Signs of an Inexperienced Programmer

Top signs of an inexperienced programmer

我喜歡聽 the tech lead 講他在軟體產業的經驗。他在這方面真的是毫不保留的分享給大眾。Top signs of an inexperienced programmer 講述新人入行時展現的種種毛病。這些毛病阻礙新人的職涯發展。影片最後給出下方的意見,值得我們參考。

  • Publish small CR small diff.
  • Avoid crazy/trash code and logic. Write design docs telling people what you are building first.
  • Don’t just code for coding’s sake. Look at the forest, not the trees.
  • Be productive. Submit a CR every day to keep you on track.
  • Swallow your pride. Listen to and learn from people.
  • Be respect to your tech leads.