大家好,我是卡米哥,LINE 官方認證的 API 專家,LINE API Expert。目前在維護基於 Rails 的 LINE Bot 框架 Kamigo。
今天要跟大家分享的是軟體開發者的培養,主要說明從剛入門的軟體開發者到成為資深軟體開發者,在過程中所經歷的事情,以及我們應該如何培養一個新進的軟體開發者。
以下由我個人的學習歷程作為說明,從我寫下第一行程式至今已有約 20 年,應該有點資格來說說這件事。
程式設計入門
我學習程式設計的第一步,就是學會寫出能滿足需求的解決方案。一開始學習怎麼透過程式語言叫機器工作,光是理解程式語言本身的運作方式,就可以讓我耗盡腦力。
大部分的時間都消耗在把需求寫成機器工作的步驟,以及把工作的步驟寫成程式碼,在這個階段裡,我能實作出第一個版本的解決方案。
開始建立價值觀
在學會寫出會動的程式之後,一開始會接觸到的價值觀,通常是如果能用越短的程式碼行數或字數就讓程式能動起來就越好,因為這表示我開始可以實作出多種版本的解決方案。
當時的我很天真的認為最短程式碼的解決方案就是最佳解。隨著時間的演進,我遇到的問題開始變得更難、更複雜之後,我的價值觀就逐漸開始產生了轉變。
對同一個問題能提出越多種解決方案就越強
價值觀:程式碼執行成本(效能)
隨著問題的資料量開始變大之後,程式再也不是啟動的瞬間就跑完了,而是開始要等機器在那邊運算,有的時候只要程式碼一個沒寫好,程式就會跑個不停,等到天荒地老都沒跑完,而我也無法預期到底程式會跑多久。
在這個階段我開始重視效能,開始接觸資料結構和演算法,了解到程式語言執行時是需要消耗機器的運算和儲存資源的,而且可以透過時間複雜度和空間複雜度來初步估計執行程式所需要的時間,以及比較兩種演算法之間的優劣。
識別出可省略的運算、去除重複運算以及快取是加速的秘訣
價值觀:程式碼修改成本(可維護性)
在學習的過程中,開發的專案有大有小,有些專案會持續的增加需求,而有些不會。我在那些持續增加需求的專案上發現到新的問題,那就是每當複製程式碼來完成實作的時候,就會導致下次修改所有相同程式碼段落的成本上升。很多案主除了增加需求之外,還會叫我最後再改回原版。
從經濟的角度來看,如果我能夠降低下次修改程式碼所需要的時間,或者將程式很快的改回上次的版本,那麼就可以有效的增加我的產能。這時候就會需要使用版本管理工具,或者用可抽換實作的架構來寫程式。
價值觀:程式碼理解成本(可讀性)
案主常常會說:「這只是要增加一點小功能,應該很快吧?」
對,也許只是加一行程式碼就能解決的問題,卻因為程式碼寫的太亂而在讀懂程式碼上就消耗了大量的時間。隨著專案的規模越來越大,需要讀的程式碼也就越來越多。
每次寫新的程式碼就像是在玩疊疊樂一樣,疊得越高就越來越不敢加程式碼,最後整個專案就會面臨無法繼續新增功能的情況,因為要加入任何一個新功能所需要的時間太多了。
而要解決這個問題的方法就是學會正確「整理」程式碼的方式,將程式碼以有系統的方式收納到不同的段落、檔案、資料夾、套件中。養成良好的收納習慣,可以讓我在需要找某行程式碼時快速的找到。
關於整理的方法,除了參考Kent Beck 的實作模式之外,其實也可以參考怦然心動的人生整理魔法,很多概念其實是可以跨領域互通的。在這個階段會開始使用類別、模組、命名空間、註解、文件、採用良好的命名方式等。
學會刪掉沒用到的程式碼是最重要的
價值觀:程式碼彈性
有些時候,我們是有機會可以預測未來需求的生長方向,不論是物件導向程式設計還是函數導向程式設計,其實都是將一體成型的程式碼(神之物件),改為以設計零件的方式,在執行時期才將零件組裝成想要的機器。
這其實有點像樂高,或者說像組電腦,我們可以透過設計主架構以及更換零組件的方式擴充機器的功能性。我在這個時期了解到應該要觀察需求的生長方向來設計可抽換實作的架構,最理想的情況則是讓所有新的需求都寫在新的檔案裡。
在有了設計架構以及零組件的能力之後,程式碼的彈性就大幅提升了。這個階段會接觸到物件導向原則和設計模式,應該把注意力放在物件導向原則上,設計模式是讓你看看人家怎麼設計零組件的,因為程式碼彈性並不是最重要的價值觀,應避免過度使用設計模式。
價值觀:程式碼測試成本
隨著時間的推移,價值觀不斷在變化,有很多因素可以導致我覺得以前寫的程式碼是爛程式碼,每當隔幾個月回頭看到自己寫的程式都會覺得:「幹!這誰寫的爛程式碼!」於是又重寫。
每次重寫時就會不免俗的一定要改壞一部分之前寫好的部分。
事實上,每一行程式碼的變動,都有可能導致任何一個功能壞掉。而規模越大的專案就越需要每次都重新測試。在這個情況下,測試的成本可以很明顯的被注意到,寫自動測試就變成勢在必行的事了。
價值觀:保留修改權
當我們面臨 A、B 兩種解決方案時,可以先考慮從 A 改為 A’ 的修改成本,以及從 B 改為 B’ 的修改成本,我們永遠可以選擇修改成本較低的解決方案。例如所有靜態的東西改起來都比動態的簡單,例如修改一個類別的變數名稱永遠比修改資料庫表格的一個欄位名稱容易。
沒有副作用的東西改起來遠比有副作用的東西容易,避免高耦合也是非常重要的,高耦合的東西修改成本極高,例如全域變數、public method、已經公布的 API 等,這類型的東西,你會無法預測你的修改將要涉及多大的規模。
其實這裡討論的價值觀,很多都可以應用到程式語言的領域之外,也可以拿其他領域的價值觀到程式語言上應用。
價值觀:保留反悔權
當我們面臨 A、B 兩種解決方案時,可以先考慮從 A 改為 B 的反悔成本,以及從 B 改為 A 的反悔成本,若其中一個選項的反悔成本是可接受的,則我們永遠可以優先選擇反悔成本較低的那個,從而保留反悔的機會。若兩個選項的反悔成本都是可以接受的,那麼就隨便選一個吧,降低在這個問題上思考的成本才是重要的。
例如在保存圖片時,應該保存原始圖片,還是保存失真壓縮後的圖片?應優先考慮保存完整圖片,系統撐不住再考慮「增加」失真壓縮的圖片副本,最後的最後才去考慮「不保存原始圖片」。
在資料庫設計的時候,應該優先保存資料的完整性,這個概念與保存原始圖片相同。若有效能需求,我們有很多方法可以讓系統變快,例如在各個層級使用快取、使用 Materialized View、分散式資料庫技術、OLAP 相關技術等。
存了資料後來發現真的用不到的時候,直接刪掉就回去了,但是沒有先存好,在之後卻要用到的時候,要去哪裡生資料?
避免做出悔不當初的選擇
價值觀:學習前人經驗
因為我寫程式很快,所以在我小時候面臨一個小需求時,會覺得自己實作一下很快,與其花時間調查別人寫好的開源套件,不如直接把時間拿來實作,也許實作只要 1~2 天,而調查套件本身就需要 1~2 天,還不包含串接的部分。
當時的我不擅長閱讀別人寫的文件以及程式碼,所以在遇到需求時,就呈現一個自幹王的狀態,但是隨著需求慢慢的增加,到最後經常變成自己自幹了一個很複雜的套件原本都寫好的功能,而且 bug 一堆。
後來發現其實一開始就去看其他人實作的套件,可以注意一下這些套件滿足了什麼其他的需求,以及他們怎麼設計串接介面,這些都是我自幹時,未來可能會遇到的需求以及解決方案。
在把別人的套件看熟之後,其實也不需要自己寫了,直接用就好了,如果沒有完全滿足需求,再考慮自己實作,或者直接加入套件的維護團隊。透過閱讀套件原始碼來學習會遠比自己實作程式碼,自己維護還要快許多。
一開始會以為只會用套件做東西的人很弱,能自己實作才強,但現在完全不會這樣想。只會靠自己累積經驗的學習方式最慢,能吸收他人經驗來加速學習,站在巨人的肩膀上。
只會自幹不厲害,學會善用工具才厲害
完整的價值觀
在許多互相衝突的價值觀當中,我們應該如何取捨?在不同情況下,價值觀的優先程度也會跟著不同,例如新創公司大多選用 Rails 是因為用戶數接近 0 的情況下,以開發速度為優先考量,效能則不重要,而在用戶數高的情況下,同一段程式碼被執行的次數開始變多,效能的重要性才會開始顯現出來。
以此類推,在不同的情況下,價值觀的優先序也會跟著不同,例如開發完成就不會再增加需求的小專案就不需要考慮修改成本。在大多數情況下我會優先考慮可維護性、可讀性,再來是彈性,最後才是效能。我認為效能只在有真正需要(有人客訴)的時候,再針對瓶頸做優化就好。
並不是說效能不重要,如果有某種解決方案可以兼顧所有價值觀,那這就是最佳解,但如果需要取捨,則第一個被捨棄的通常是效能,而效能有各種不同手段可以提升,就看需求到哪。
應依照情況從 N 種解決方案中選出最佳解
通靈能力
案主經常會提出多個需求,而這多個需求是互相矛盾的,或者不完整的,此時應該要引導案主說出想要的需求,或者說,我們應該先幫案主想好可能會遇到的問題以及對應的解決方案。
在這個階段我學會與案主溝通的方法,案主是外行人,根本不知道自己需要什麼,我們不應該問案主申論題,而是問案主選擇題,最好是二選一或三選一,且推薦其中一項就好,提供太多的選項反而會造成案主的選擇障礙。如果是問連續的是非題,那個過程跟擲筊沒兩樣,對,其實你就是在通靈。
如果是瀑布式開發,在這個階段可以考慮植入一些專業的建議,例如把你所有想得到的需求都開出來,對案主做 upselling,以消化案主的最大預算,透過問問題的方式,讓案主了解到有很多的細節都是要考慮的部分。
如果是敏捷式開發,應盡可能的砍案主規格,讓案主說出最重要的核心功能,先實作核心功能,就迅速上線以獲得用戶回饋。比起案主的回饋,用戶回饋可能會更實際一點,應以打造 MVP 為目標。
這部分可能已經有點偏離開發者該做的事,但身為開發者,至少要能夠確認技術上的可行性。
協作能力
以上提到的都是單人開發時的情況就會遇到的問題,而有些議題則是在多人開發時才會遇到,融入一個開發團隊是身為軟體開發者必備的能力,Git 之類的版本管理工具這種必備的能力就不多解釋了。
一個專案在開發的時程很長的情況下,隨著我的技術等級不斷提升,新功能自然就會使用新學到的更有彈性、更好維護的寫法來寫,最後整個專案的程式碼會呈現各種不同技術等級的狀態。除非每次技術力升級的時候,都把整個專案重寫,否則一個專案有不同技術等級的實作,是必定要承受的後果。
以下使用同居來比喻這個問題,一個專案就是一個房子,不同等級程式碼則代表不同的髒亂程度,每個人自己單獨維護的程式碼,就是各自的房間,而共同維護的程式碼就是公共區域,例如客廳、廚房、陽台、廁所等,開發新功能就相當於在擴建房屋,技術升級就是改變「乾淨」的定義。
每個人對於「乾淨」的定義不同,這產生了第一個問題。有些人有潔癖,他們會將環境打掃得很乾淨,他們也會要求同居者必須維持環境的「乾淨」的最低限度,但到底什麼是「乾淨」呢,這值得大家先獲得共識,那就會是環境規約。
如果房子很大,但是居住的人很少,打掃的成本太高,導致在改變環境規約時,仍需要容忍髒亂的環境,因為忙著蓋新房子,沒有時間回頭打掃,通常回頭打掃的時候就是危老重建了。
不論你技術等級多高,都有可能接到危老重建的工作,所以身為一個資深的軟體開發者,要有能力維護任何技術等級的程式碼,且在維護的過程中讓程式碼升級到滿足最新的環境規約。
環境規約應該被寫成文件,讓大家可以參閱,但若環境規約很冗長,大家即使看完了也無法記得的時候,可以靠 Linter 來把關,Linter 可以即時檢查程式碼是否符合所有環境規約,好一點的甚至可能做到自動打掃的功能。
無法將髒亂環境打掃成乾淨環境的軟體開發者,不是資深的軟體開發者。
禮貌、尊重和同理心
我曾經遇過一個情況,當時我是主管,帶了兩個人小明、小美,小明實作了功能 A,而小美在一段時間後將 A 改寫為 B 以符合新需求,小美說:「A 的實作很爛,所以我重寫了 B」,引起了小明的不滿,小明說:「B 的設計有缺陷,A 有 A 的好處」,我身為主管進來調解,在聽完 A 跟 B 的實作後,我提出 C 設計,同時具有 A 跟 B 的優點,當然小明跟小美就都閉嘴了。
後來隔了一段時間,小明提離職了,我猜原因可能是小明寫的程式碼全被小美或我改掉了,這導致他無法在這個過程當中獲得成就感。也許每個人的技術等級不同,應該在修改別人的程式碼前,先進行溝通和討論,如果可以,應儘量讓同一個人維護同一段程式碼。
但在開發的過程中難免會改到其他人的程式碼,或者被其他人改到自己寫好的程式碼,如果你不知道該怎麼做,可以回想一下同居的比喻,當你跟人同居時,不應該隨意拿別人東西或拆別人房子,任何事只要與對方有關,就應該先經過對方同意後再做。
身為資深軟體開發者,應該要有能力培養技術等級低的開發者,而不是排擠或邊緣化他們。一個有能力培養新進開發者的資深開發者是更有價值的。在遊戲裡,30% 團隊戰鬥力加成的靈氣,通常是很強的技能。
能不能好好相處也是非常重要的
備註
我可能還遺漏了許多重要的內容,各位讀者不應該將以上內容視為完整內容。
看完請拍手分享,謝謝