2023-10-19|閱讀時間 ‧ 約 17 分鐘

閒談軟體設計:架構師難尋?

會有這篇是因前陣子,和 91App 的首席架構師 Andrew 在母校北科旁伯朗咖啡聊天。91App 的 HR 看過我《閒談軟體設計》某篇文章後,問 Andrew 這位作者如何?碰巧 Andrew 也看過閒談軟體設計:Async everything?,加上 Terry 認識我,於是牽線促成碰面。我蠻意外的,因為這系列文章,觀看樹很難衝高 (拜託,幫忙多分享),有時都會想還要繼續寫下去嗎?沒想到會有這種展開 XD

聊天聊蠻多的,主要是對架構設計的想法,以及實務上會考慮到那些東西,但有個有趣的話題是:軟體架構師很難找?在學校最後幾年的研究都和軟體架構有關,所以剛畢業時,我確實很想找架構師的職缺,但軟體架構師的職缺並不多,當時差不多是 Android 和 iOS App 最夯的時候,招募的職缺大多是 App 工程師,再來是網頁工程師 (那時 front-end 和 back-end 還沒有分這麼清楚),若堅持找軟體架構師,我恐怕會餓死 (主要是不太會有公司願意找剛畢業的人擔任架構師)。

我首份工作是 GSS 的系統分析師 (系統分析及網站系統開發),第二份工作是遊戲橘子某個部門的 Android 工程師,後來兼 iOS 工程師和 PC 應用程式工程師,最後升到軟體架構師兼 Scrum master,但若仔細看外面各公司的職缺,確實很少會看到想要找軟體架構師,大多是資深工程師、Team lead,或是某個專業技能 (front-end、back-end 或 app) 工程師。加上很多公司在規模未起來前,還在找方法存活下去的情況下,大多也不會想到需要軟體架構師。我只有一次 (可能是本人太菜了),LinkedIn 上的獵人頭介紹我誠實蜜蜂軟體架構師的缺。

我覺得吸引不了工程師往軟體架構師的方向養成,除了市場的風向,我個人認為軟體架構師沒有很明確的養成之路,所以有些人可能不知道怎麼朝這個方向發展。這裡,我倒是可以分享自己在往軟體架構師做了哪些努力。

基本功

一般來說,我覺得 design patterns架構 patterns、SOLID 等原則以及抽象能力是很重要的基本功。若讀《Clean Architecture》會發現,前面花了不少的篇幅介紹 SOLID、REP、CCP 和 CRP 等原則,但繼續往後看,就會發現,即便一開始不使用 clean architecture,只要能抓住上述幾個原則,程式就會有一點點 clean architecture 的雛型。

這些基本功在學的當下,其實不見得會有感覺的,是程式規模寫到一定的程度後,回頭再去看程式碼,再去回顧這些原則,才慢慢會有感覺,我只能說很慶幸,在離開學校前就能夠累積一定規模的軟體開發經驗,也很感謝母校的老師和助教花那麼多時間幫忙 review 設計與程式。

但即使到業界後,每年回顧自己過去寫的程式,雖然風格有點固定,但還是能找到可以改進的地方 (為了寫這篇,回頭看過去的 side project,嗯,還是有改進的空間),還是花點時間回去看一些已經看過的書,design pattern 應該讀過不只三遍,雖然看的重點已經不是 pattern 怎麼用,但每次都能有些新的想法。

廣泛涉獵

除了自己本身最擅長的技能外,多多涉獵一些新技術,自己喜歡的多一些無所謂,像我本身就一直有在專注 Java 領域的新技術。但即使是自己不喜歡的,也多多少少看一下,所以我偶而還是寫一下前端的東西,淺嚐一些沒寫過的動態型別語言。

切記,一定要看缺點,沒有任何 pattern、框架、架構是完美的,像 pattern 有所謂適用的 context,在不合適的 context 下使用,不是不行,但程式看起來就很彆扭,或是 over design 的感覺。所以,我個人不太相信那種,你應該用什麼方式寫程式,會有什麼什麼優點,但卻通篇不提有什麼缺點的文章。

若是有原著,可以的話還是看原著。

這幾年看很多人在網路上分享技術或是 pattern 的文章,這樣很好,畢竟很多原著是厚厚的一本書,不見得有那麼多時間 (真的,書櫃裡一堆書找不到時間看,DDD 還躺在書桌上只翻幾頁),但有時候看網路文章會覺得:這好像跟我對於某個事物的見解不同,那是文章作者對於原著的解讀,若沒看過原著就不會發現不同,並不是說我的見解一定是對的,但沒看過原著直接相信某人對於某事物的見解,也是很危險的。

忍住不追流行

要注意,除非燒自己的錢,不然公司的專案或產品不是讓你練功的地方,請用 side project 練功;除非是一人公司,不然嘗試任何新東西之前,請先和團隊溝通,並確認轉移計劃,讓 code base 有一定的一致性。當聊到好學的團隊成員喜歡在公司的產品裡試新東西,這情況特別常見於前端,因為這幾年前端常常冒出新框架或技術,聊天時有個有趣的比方:看 commit log 像是在看年輪考古,所以可能半年前用 x 技術、幾個月後又變成 y 技術、然後又開始試 z 技術。

專注於商業邏輯,而不是技術,新技術不斷推出,但如果拉長時間來看,很多新技術冒出,卻一直在解決相同或相似的問題,只是用不同的觀點或是用不同的方式解決相同的問題,例如,過去幾年,RESTful API 是設計 API 的主流,但最近幾年 GraphQL 很熱門,GraphQL 確實有很多的優點,但不代表就要將既有產品的 API 轉成 GraphQL,應該想的是用了 GraphQL 是否有解決原本解決方案無法解決的問題?

再舉個例子,先前有很長一段時間,noSQL 儲存方案超級熱門,像 Redis、MongoDB、Cassandra 等,那個時候好像用傳統 RDBMS 就很 low,但等到換過去或是使用一陣子後才發現,沒有 ACID 很多關鍵的資料一致性很難達成,這就是沒有思考 noSQL 和 SQL 方案各自適合與不適合的結果,最近我的觀察,又開始有不少人主張用回 SQL 方案,其實那都不是重點,找到適合的方案才是重點。

像下圖,隨著時間推移,會有很多技術冒出,頻繁地在產品中追逐新技術,若沒有一個好的計畫與整體方向,會讓產品的程式碼變成碎片化,碎片化會增加維護的成本與新手學習的成本。並不是一個產品永遠不導入新技術,而是不論想導入的技術在哪個階段,導入前要有評估、方向和計畫,不然投入的資源會變成無法帶回效益的浪費。

但新技術還是要學!有空寫寫 side project 吧。

取捨

我想寫過程式的人都知道,就算沒有架構,功能一樣寫得出來,因此決定軟體架構的因素往往是非功能性需求,這也是為什麼前面提到廣泛涉獵時,要知道每個技術或是架構的優點與缺點,適用的 context 是什麼?有沒有類似的替代方案。如此一來,不論是設計新產品的架構,或是要對既有產品做架構的調整,才能根據不同面向,找出平衡的解決方案,然後能和團隊說明取捨的依據,有興趣可以找 ATAM 練習看看。

另外有一個東西,因為不太好量化,所以有時候取捨時容易被忽略,那就是成本,成本有很多種,例如維運成本、學習成本、維護成本等。維運成本可能可以從雲端服務的帳單可以看出大概 (但處理線上緊急事件或客服就比較難有簡單的數字),團隊的學習成本和後續產品的維護成本就很難估算,不過在做架構設計時,還是得考慮進去,例如:採取較簡單的架構,降低學習成本,容易測試的架構讓後續的維護成本降低。若使用 ATAM 也可以把這些納入條件一起評估。

有趣的是,有時候取捨的關鍵,往往不是自己能控制的,像是預算、時程或是團隊成員,別花太多時間對自己無法控制的事抱怨。

POC

在離開遊戲橘子後,幾乎都待在新創公司,因此職稱通常都很大,但還是要寫程式的,不是當了架構師後就不用寫程式,不寫程式會慢慢脫節,變成只會嘴砲的架構師。我很喜歡《建構微服務》裡的一句話:

架構師有責任確保系統也能讓開發人員在那裡安居樂業

架構的設計不是架構師自己看爽的,要能讓團隊中的工程師都能有一定的生產力。假設是一個新的產品或是一個新的專案,在做完種種的取捨,確定架構的方向後,架構師是有必要做出一個 Proof of Concept (POC) 來確保整體方向的可行性,但要做到多大呢?或多完整呢?網路上有蠻多種 POC 的說法或作法,以我自己來說,我會把我想表達的概念都實作出來。

像先前有個新網站的專案,目的是要取代已經上線的舊系統,由於舊系統常被抱怨常常改 A 壞 B,因此 POC 想表達的重點便是為 Model + View Model 加上測試來避免這情況,POC 中所有的 React 元件事件的處理全導向 View Model,View Model 將商業邏輯導向 Model,並將結果轉成 State 通知元件更新,以及 View Model 與 React 元件如何綁定。由於所有的程式 Model、View Model 和 React 元件彼此獨立,測試相對好寫,很容易就能做到 100% 的測試涵蓋率,雖然這不保證沒有 bug 喔!若需求理解錯誤,即使通過測試還是 bug,但是當之後做任何修改有問題,測試都能立即捕捉,這便是我想向當時團隊成員表達的想法。話說離開那個專案一陣子了,不知道當初留下的東西有沒有長歪掉?

寫文件

不知道有沒有統計過,但確實認識很多工程師是不愛寫文件的,連 API 文件都不願意好好寫的人,要求寫設計文件更是不可能了。加上前陣子,敏捷宣言裡 Working software over comprehensive documentation 被錯誤引用,認為不寫文件才是敏捷,但實際上是指能動的軟體比對開發沒有幫助的文件有用 (受過 CMMI 文件訓練的人應該都知道)。有人說程式即是文件,我覺得想這麼說的人,先確定自己寫的程式碼易讀性高嗎?而且很多高階的設計光透過看程式碼要花很多時間才能釐清,有文件可大幅縮短理解設計的時間。

對我來說,任何較大的 (架構) 設計施工前,我都習慣寫文件,一來是幫助自己整理思緒,確保自己不會在一邊思考一邊寫程式的當下迷航;二來可以思考施工的順序,這在架構重構時十分有用,思考施工的順序可以分析對系統造成的影響,如果是已經在營運的系統,這分析更是重要,理想的順序可以避免系統停擺。

至於文件要寫些什麼內容,我倒是沒有一定的格式,看當下要處理的問題是什麼?一般來說,有幾點是會寫下來的

  • 目標、動機或要解決的問題
  • 可能的解決方案
  • 取捨的因素 (force、非功能性需求)
  • 抉擇的理由
  • 粗略的設計 (class diagram、sequence diagram 或是 pseudo code)
  • 簡單的 plan

大概就這樣,不是寫那種很制式的 CMMI 文件,這文件在自己施工時其實也很有用,確認自己有沒有在軌道上,最後是文件可以幫助指導新人快速進入狀況。

寫技術部落格也是不錯的 (笑)

說服與指導

架構師有很多時間是在說服與指導團隊成員,說服團隊為什麼這樣的架構設計比較好,開始施工後要指導團隊成員,不偏離設計。

這讓我想起來之前有 SDK 開發經驗 (參閱閒談軟體設計:設計抉擇的因素),當時 SDK 開始時,我針對幾個點把優缺點分析在白板上,然後和團隊說明,事實上我當時有偏好的設計但並沒有百之百決定要用哪一種設計,想聽聽團隊的聲音,在一番討論後,團隊選擇的方案就是我當初偏好的設計,這樣的好處是,團隊成員都清楚接下來為什麼是這樣設計,寫出來的程式自然不容易歪掉。

設計的好與壞沒有絕對,像我就不太喜歡 switch case,會盡量避免 (參閱閒談軟體設計:Switch 壞味道),但這幾年看 Swift 的程式碼就有大量的 enum 和 switch case。程式碼的易讀與否也會因人而異,你覺得這樣寫比較好懂,但別人可能看不懂 (像是 declarative vs. imperative,我個人也沒有覺得哪一邊一定比較好懂)。寫程式有趣的地方就是同樣的功能卻可以有不同的寫法,通常成員也各有所好,故幫忙 code review 或指導時可以給建議,說明為什麼這樣改比較好,也聽聽原作者的想法,除非是不符合 coding convention、違反架構精神或是真的很差的寫法,不然別太堅持團隊成員一定要照你的方式寫。

聊天時曾聊到過去在遊戲橘子的經驗,當時 Android App 的架構由我主導 (參閱閒談軟體設計:Android App Architecture),後來要開發 PC 版,由於部門內的人力不夠,所以找外包人員駐點幫忙開發,由於當初找的外包人員是自己挑過的人,只要稍微說明一下,很快就能上手,加上核心模組已經在那裡不用重寫,半年就完成了八成左右的介面 (Android App 開發一年半),我對當初的架構還蠻有信心的。離開遊戲橘子後,PC 版的外包人員也換人了,後來輾轉聽到新外包人員抱怨:為什麼程式要這樣寫,總之抱怨很多,聽到當下我能說什麼呢?只能笑笑~離職前有留下設計文件,但少個人指導新外包人員,確實還是有差。

不長歪的演進

先不論是否以 Agile 的方式開發產品 (不是用 Scrum 或 Kanban 就是 Agile),現在很少軟體開發是架構師把所有的東西都設計好後,開發團隊一個模組一個模組進行開發,然後再整合。通常是針對一個功能 end to end 在既有的 code base 加入程式碼,完成後就上線或是繼續開發下個功能,這種情況下,最容易的就是變成一盤義大利麵...

同樣在《建構微服務》書中

架構師必須改變想法,放棄想要打造最終完美產品的思維,改為聚焦在協助建立可從中衍生出合適系統的框架,並且隨著我們了解更多資訊而持續壯大。

因此,架構師腦中還是要有一個藍圖,除了跟團隊說明如何加入實作,要不斷和既有的程式進行比對 (大量的 code review),確認沒有歪掉,或是歪掉時要與開發團隊一起修正外。更重要的是,腦中的藍圖,也要隨著新的需求、外在條件、內在條件,進行修正。

和技術債共存

這句話我忘記是在哪看到的,但很有趣:

存活下來的產品才有技術債的問題!

如果產品失敗了,公司無法獲利,事實上也不用處理技術債了。因此,通常是存活下來了,開始有點餘力,發現當初在衝刺時留下一些 (很多) 問題,現在想開始處理,這時常常會想大刀闊斧,想改很多東西,但這其實很危險。

《派遣女醫 X》有個場景是因為風險太高,很多醫生勸大門醫生終止手術,但大門總是說:『我絕不會失敗』,硬是繼續手術,然後手術成功了。但現實的軟體開發中,即使是手術成功,卻有可能把公司搞掛了。

所以面對技術債,不是視而不見,畢竟你不去處理它,它會一直以不同的形式來煩你,像是降低生產力、一直有 bug、不敢改某個功能。而是,要有計畫地重構,不論是低風險驅動還是高風險驅動,都要小步前進。這時,單元測試及整合測試就非常重要,任何修改有自動化測試在都會放心許多,若沒有測試,那...先補測試吧!沒有計畫就大改特改,常常會浪費時間,還越改越差。

雖然用這個例子有點怪,因為我覺得那個專案沒甚麼技術債 (自我感覺良好)。剛離開遊戲橘子的第一個新創,當時幫關島觀光局開發一個 CMS 加上網頁和雙平台的 App,後來公司被併購,這專案只好找其他外包公司接手,幾個月後,輾轉聽到接手的外包公司說,這專案很好維護,聽了就很高興,畢竟交接時,server 端可是有 92% 的測試涵蓋率。

更高的視野

剛開始當工程師時,眼前可能只專注在某個功能的開發,這時會很專注程式碼的好壞,這不是壞事,但太過專注會看不到其他重要的事,試著用上很多 pattern、新技術,寫出很精巧的程式,但卻不見得是最佳的程式,這都是很正常的學習之路。當把視野放大一點,開始專注整個產品,會開始思考怎麼維運 (維運不是只有 oncall),測試為什麼那麼重要。再把視野放大一點,可能會看到組織或是團隊,這時對架構的思考就考慮到團隊的接受度,以及該怎麼引導團隊。

若再把放寬一點,會思考怎麼幫公司用最小的成本並維持一定的品質 (這裡是指程式碼本身,功能是一定要對),然後能賺錢,這時候可能就會覺得系統寫得簡單比精巧好,有時太精巧的程式反而在面對需求變化時,難以修改。甚至,會思考怎麼讓系統提供數據,協助做出很好的商業策略。也就是說,即使是一個軟體架構師,也不能只看程式、架構,也是要看商業策略的。

小結

以上就是這幾年我對於軟體架構師的心路歷程,上述不保證讓你成為軟體架構師,但希望會對軟體工程師職涯有幫助。也希望台灣的軟體公司能稍微多注重一下軟體架構,甚至能像 91App 不只工程師團隊,還有軟體架構團隊。

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.