# 遺留代碼經濟學 - Terry Yin :::success [投影片下載](https://s.itho.me/modernweb/2018/day2_1030%20-%201110%20Terry%20Yin%20-%20%E9%81%BA%E7%95%99%E4%BB%A3%E7%A2%BC%E7%B6%93%E6%BF%9F%E5%AD%B8.pdf) ::: [toc] ![](https://i.imgur.com/UkrjMvs.png) 今天很榮幸給大家帶來這個叫做「遺留代碼經濟學」這麼一個話題。 ![](https://i.imgur.com/OEz5cCp.png) 我叫 Terry, Terry Yin, 我來自中國大陸,但我住在新加坡。 這次是第一次來台灣,我跟我女兒一起在這邊,之前已經待了兩天了,覺得還蠻好玩的。 我自己是一個幾十年工作經驗的軟件開發者,然後呢我也是兩個孩子的父親。 我在一家叫做 Odd-e 的公司工作,我們在台灣就只有這麼一個同事,就是這個露出半張臉的這個人,他的名字叫做 ,也叫做 91,業界小有名氣。 走了幾天,開始想我兒子了,這是我的兒子,那個時候大概三歲左右吧。 ![](https://i.imgur.com/4LahsrR.png) 知道他手裡拿的是什麼東西嗎?叫啥,它叫做 Coffee Grinder,用來磨咖啡粉的。 看一下他的表情啊,他到底是開心還是不開心呢? 好像是有點不太高興啊,首先要先解釋一下,不是我要他給我磨咖啡粉啊,是他看我在那邊磨,誒挺好玩的,所以也想試一試。 這個磨咖啡粉,對我們大人來講,是挺簡單的一件事,對三歲的小孩來講,有點難。 他搖不動,所以他不開心。 誒下一刻,看見沒有,開心還是不開心? ![](https://i.imgur.com/oofDSUF.png) 不開心 開心 不開心 開心 ![](https://i.imgur.com/4LahsrR.png =145x81) ![](https://i.imgur.com/oofDSUF.png =145x81) ![](https://i.imgur.com/4LahsrR.png =145x81) ![](https://i.imgur.com/oofDSUF.png =145x81) 誒為什麼突然之間開心起來了,然道說突然之間他力氣變大了嗎? 誒不是的,他發現誒誒誒雖然這樣搖不動,但是這樣 (反方向) 可以搖得動了。:satisfied: 所以他就開心起來啦。 對他來講啊,他想要的是這種快搖的這種快感,至於說出不出咖啡粉,關他什麼事情啊。對不對? 誒這時候大家有沒有發現,我們很多人,開發者也是這樣,我們關心的是速度,是 productivity,但到底你有沒有產出,有沒有價值,關我什麼事情。有沒有? :::success Developer 關心的是產出速度,而不是用戶價值。 ::: 講這麼一段小話題主要是想我兒子,跟主題不一定貼近啊。 ## 什麼是 Legacy Code (遺留代碼)? ![](https://i.imgur.com/JKecn0t.png) 今天要講的是遺留代碼,那什麼是遺留代碼? 嗯,我經常會講到這個話題,也會得到很多很多的回答,經常會問這個問題。 有人說,沒有測試代碼的是遺留代碼 有人說,時間很久的是遺留代碼 也有人說,別人寫的就是遺留代碼 :::success 何謂遺留代碼? - 沒寫測試的? - 寫好很久的? - 別人寫的? ::: 誒,我也有一些自己的理論。 在我介紹我自己的認識之前,大家肯定關心,我的這個想法,是我坐在家裡想出來的?還是基於某些實踐來的呢? 所以我把這個背景跟大家介紹一下。 ### The Odd-e CSD Class *[CSD]: Certified Scrum Developer ![](https://i.imgur.com/mh0xEAH.png) 我自己的確是有很多的工作經驗,包括很大型的軟件開發,????二級三級四級網絡都有參與,都是幾千人的這種開發產品。 小的產品網站我也做,同時我也做過一些資訊工作,這些呢都不是適合於你做實驗的場合。 同時我們做很多開源軟件的開發,我有自己開源的項目,也經常會給其他人開源的項目。 這些都不是你做試驗的場合。 我們有幸呢,經常會講個課,這個課叫做 CSD (Certified Scrum Developer),這個課蠻有意思的。 我們花一週的時間來上這個課,然後呢這一週的課,我們是講 Scrum,我們希望的結果是讓大家體驗一下,Scrum 到底是怎麼一回事。 那這樣一來呢,我們用這一週啊,只跑一個 sprint。 一般來講呢,有十個左右的參加者,那這十個人呢,它會組成一個團隊,在一個產品上做開發,一個 sprint。 ![](https://i.imgur.com/MmwAfCI.png) 也就是說,這不是一個模擬,它是一個真正的 sprint,那麼跟這位老兄啊,不太一樣。 這位老兄啊,他只在這,這個這個這個ㄜ,搖著只是為著好玩。 ![](https://i.imgur.com/MuolyVj.png) 那麼這個團隊呢,它同時要迭代來 deliver ㄜ 溝通 feature。 ![](https://i.imgur.com/2LE6jaY.png) 而更有意思的是什麼呢?當下一個團隊來的時候,他們並不是重新 build 一個新的產品,而是接著前面一個團隊的產品,接著做。同一個產品,同一個 background,上面一個團隊做到哪裡,他們接下來繼續做。 :::success 課程成員組成 Scrum 團隊玩真的,負責一個 sprint 的開發,下一個團隊要接手做 ::: 這就像一個公司,它有 100% 的離職率,我們一次招來十個人,在這邊工作了一星期,哎呦,全都離職了。然後我們再招來十個人,他們在這邊也只工作一星期。 :::success 模擬公司中開發人員及團隊組成的流動率 ::: 那麼他們看這代碼的時候,他要加新 feature,他就說,這個代碼是幹嘛的?能不能改?可不可以這樣改?我可不可以打一個電話給前面那十個人? 畢竟是一個課嘛,恐怕這個可能性不太高,對吧? ![](https://i.imgur.com/70ZOuSk.png) 這個時候啊,這種經驗就給我們驗證了,更加驗證了我們對於軟件開發的一個認識。 我們認為啊,軟件開發它主要是這麼兩件事情。 一個,軟件開發它不是只有寫代碼這麼簡單,它的本質是獲取知識。 還有一個方面呢,就是要 preserve knowledge,我想我把它翻譯成,叫做保存知識吧。 :::success 軟體開發的本質:acquire and preserve knowledge (獲取以及保存知識) ::: 就是說,你把這個軟件呢,怎麼去做什麼東西,跟怎麼做,這些知識獲取了之後,你有沒有以一種什麼形式把它保存下來。 那麼下一個人,他再來繼續在這軟件上面,在這產品上面開發的時候,它可以理解你的這個知識。 ### Knowledge of the Software Product ![](https://i.imgur.com/aMzSaLC.png) 啊,這個知識啊,我把它分作兩個方面。 ![](https://i.imgur.com/Ic5sEua.png) 一個是問題域,另一方面呢是解決方案域。 :::success 知識類別:問題域、解決方案域。 ::: ![](https://i.imgur.com/PVJMPlg.png) 我們怎麼樣在問題域獲得知識啊,或者是創造知識,那是通過理解我們的業務,獲得需求是什麼,然後把需求細分下去,我們知道要做什麼東西,通過這樣的一個活動,我們獲取了對於問題域的知識。 :::success 問題域的知識:理解業務需求,分析並理解 → 知道要做什麼 ::: 那麼解決方案域的知識怎麼獲取的呢?我們知道要做什麼了現在,具體怎麼做呢?怎麼叮叮噹噹的把這個代碼寫出來,讓這個代碼讓這個產品工作起來?這個過程呢,就是啊發現解決方案的這個過程。 :::success 解決方案域的知識:具體怎麼做?代碼怎麼寫? ::: ### 用戶的需求滿足後,要怎麼把知識保存? 那麼好啊,現在軟件的確是可以工作了,對不對?誒,用戶的需求已經滿足了,那麼接下來,我們怎麼把這個知識,把它保存下來,把它 preserve 下來呢? 在問題域,我們要用文檔嗎?恐怕你把它寫成文檔來,也解決不了問題,因為文檔在哪裡,也是一種知識,對不對?你要把這種事放在哪裡? :::danger 使用文件? 文件在哪裡也是一種知識 ::: 所以我們希望啊,我們把我們對於問題域的知識放在我們自動化的測試文檔裡,spec 裡面。 :::success 問題域的知識保存:放在自動化的測試文檔 - 不會說謊的文件 - 測試清楚表達了產品該做什麼 (需求的 intent) - 產品沒做到,測試會失敗 ::: 那麼這些文檔呢,我們把它叫做「不會撒謊的文檔」,它解釋了我們產品做些什麼,應該做些什麼,而且我們產品不這樣做的話,會怎麼樣?我的測試會失敗。 在另外一方面呢,解決方案領域,我們通過把代碼寫得簡潔,然後它可以自己表達意圖,這樣的方式,並且呢,希望我們能把代碼,它的模型與業務模型有一個比較好的對應關係。 :::success 解決方案域的知識保存:容易理解的代碼 - 代碼簡潔 - 可自己清楚表達意圖 - 代碼模型跟業務模型有好的對應 ::: 那麼他就把這些知識,保存下來了。那麼下一個人就可以維護這些代碼。 那麼這是我們對這個開發的一些初淺認識。 那麼有了這些認識之後,那麼我對遺留代碼的觀點就是這樣子。 ![](https://i.imgur.com/8cW4Sjz.png) 那所謂遺留代碼就是說,知識你的確獲取了,對用戶有價值的產品你的確做出來了,但是呢,你沒有很好的把這些知識保存下來。這就是遺留代碼。 :::success 遺留代碼:對用戶的需求滿足了,知識獲取了,但沒有很好保存 ::: 因為跟女兒一起走,我一直又要準備這材料,所以採用了這叫做 Minecraft 的主題,感謝大家一直忍受這主題到現在。接下來就沒有了。 > 另外一個鋪墊,到現在為止都是鋪墊 > [name=講者][color=#90b7f7] ## Cost and Interest ![](https://i.imgur.com/QYvbMFE.png) ### Cost of Change (改動成本) 啊,就是經濟學嘛,題目叫經濟學,其實我也不懂什麼叫經濟學,大概就是這麼兩個方面。 一方面呢,我們關心的就是這個,叫做 cost of change。 我們提到 legacy code,不得不講到 cost of change,就是代碼的改動成本。 如果是遺留代碼的話呢,就是這條線,這條很陡的線。 但是要知道,有很多軟件產品,經過一兩年的開發以後啊,它的 cost of change 如此之高。 雖然這個軟件啊,還在用,還在賣,還在給客戶創造價值,但想要在上面做些新事情,或是甚至改些 bug 都很難很難了。 那這個時候可能就需要重寫。 那麼我們希望走的是這個比較平的這樣一條線,一開始的 cost 會比較高一點,可是我們希望呢,隨著時間的推移,cost of change 不會增加,或者是不會劇烈的增加。 :::success 希望隨著時間推移,改動成本不會增加,或者不會劇烈增加 ::: 最近一兩年有一個事情變得很流行,叫做 mob programming,我不知道在台灣怎麼樣啊,在新加坡,在日本,我都看到很多人在做這種嘗試。 原來我們講 pair programming,現在很多人在做 mob programming 嘗試,就是一組人,一個 team,五六個人,他們在一台電腦上面工作。 剛才講到 productivity 對不對? 那一組人,五六個人,在同一台電腦上面工作,那效率會怎麼樣? 可是做 mob programming 的人啊,他們之間流行一句話,他們說:「我們就指望今天的 productivity 跟昨天一樣就可以了。」 :::success 今天的生產力與昨天一樣就可以了 ::: 想想其實蠻有道理的,因為你今天的 productivity 跟昨天能一樣其實就很難了,如果你不追求這個東西啊,很有可能最後你就走到這樣一條線上去。 (從大家角度看,是這樣一條線啊。) :::info 一條指數成長的曲線 ::: ### 技術債 另外一個呢,我們經常提到的一個比喻,叫做技術債。 技術債 技術債,那麼就是說我們有意的去欠下了一些東西,???借錢了。借了錢之後呢,就是要還的,對吧? 技術債,借了,顯然是為了眼前的利益,所以有的時候,我們是有些取捨的,但是我們總是要還這個債。 除了債之外,你借了錢還要幹嘛?還有這個 Interest,就是叫做利息。 利息也有兩種,有些利息嘛很簡單,你不還的話嘛,總是要付一些小利,對吧。最後大不了還掉就完了。 還有一些債,它借來之後是利滾利,叫做 Compound Interest。 比如說,你不還的話嘛,你越晚還,你還得越多,你每次要付的利息也越大。 這些可能是我們一些取捨的,一些可以考慮的方面。 :::success 技術債 - **有意**的去欠下某些東西 - 為了眼前的利益 - 總要(早該)還的 - 還會**生利息**! - 還可能**利滾利** ::: 這才進入正題,雖然晚了一點。我演講的風格可能太習慣了,鋪墊這麼多。 ![](https://i.imgur.com/ZPghFAR.png) 我想從三個角度來講,遺留代碼的事情。 第一個是測試。 第二個是架構。 第三個是組織結構。 ## 從「軟體測試」的角度來談 Legacy Code ![](https://i.imgur.com/E3ghzEW.png) 通常來講呢,我們講 Legacy Code 的時候,會講第一個方面比較多。就是說,遺留代碼跟自動化測試之間的關係,可能會講比較多一點。 ![](https://i.imgur.com/SKVFQji.png) 為什麼呢?主要是因為這本書,這本書主要叫做 [Working Effectively with Legacy Code](https://www.tenlong.com.tw/products/9780131177055),作者叫做 Michael Feathers。 ![](https://i.imgur.com/E6o8Ypr.png =136x179) 他很多時候被我們稱為「遺留代碼之父」,不是說遺留代碼都是他寫的:satisfied:,而是說他對遺留代碼???的比較深刻。 他對遺留代碼的定義啊,就是說「沒有測試的代碼」。 > 沒有測試的代碼就是遺留(壞)代碼 > [name=Michael Feathers][color=#90b7f7] > Code without tests is bad code. It doesn't matter how well written it is; it doesn't matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse. > [name=Michael Features, Working Effectively with Legacy Code][color=#90b7f7] 為什麼這麼說呢?因為如果你這個代碼有測試的話,我就可以去改它,改進它或在上面加新 feature。 我知道我有沒有破壞已經有的東西,是吧?否則的話,這代碼就不能改。 就好像,我剛才呢,用剛才的模型來解釋一下,我獲取了好多知識,但是你並沒有把它給保存下來。這些東西都浪費掉了。 ### 遺留代碼是個大坑 ![](https://i.imgur.com/M9qPJGC.png) 如果你發現自己啊,在一個大坑裡面,那麼首先各位覺得應該做什麼? 哇,好深的坑啊 首先該做什麼啊? 有人說我要打個電話 有人說我要爬出去 有人說喊救命啊 是吧? 這些可能都不是??? 首先啊,如果你發現你自己在一個大坑裡,首先要什麼啊? 就不要再挖了嘛,對吧?為什麼你會在坑裡面? 就是因為你一直在挖啊。 現在我們在這樣一個代碼的大坑裡面,對吧? 那是因為有人一直在挖坑。 有人說,那個坑不是我挖的。 我來的時候,那個坑就已經在了。 那不管是不是你挖的,它是因為我們的 practice,對不對? 如果你跟別人的 practice 也是一樣的,你也是在挖坑。 所以說呢,我們已經在一個 Legacy Code 的大坑裡面,就不要再挖了。 :::success 如果你發現自己在 Legacy Code 的大坑裏,要怎麼辦? * 就不要再挖了! * 你若是跟別人做相同的事情,你也是在挖坑! ::: 那什麼叫做不要再挖了? 剛才我介紹 Michael Feathers 對遺留代碼的定義是什麼來的? 沒有測試的代碼。 那什麼叫做不挖了? 就是從今以後,你在寫代碼都要帶個測試。 每次我在台北買東西,他們都給我發票,對吧?一樣的道理。 :::success 要怎麼不再挖坑? - 寫測試 ::: ### 遺留代碼演算法 (Legacy code algorithm) 在 Michael Feather 的書裡面呢,他為我們提供了一個很簡單的,怎麼在遺留代碼上工作的一個算法。 ![Legacy code algorithm](https://i.imgur.com/bZE5WIz.png) 第一步,要找到改動點。遺留代碼範圍很大,那我們現在只要做一件事情,比方說是要改 bug,也有可能要加新 feature,其實這兩件事情差不太多。 我們說改 bug 好了,首先我們總要定位這個 bug 在哪裡,對吧? 在哪裡改?這個呢,沒太大的區別。 誒找到了,在這裡改,改動點找到了。 接下來幹什麼,把它改掉,對吧? 然後呢,手工測一下,誒好了。 然後呢,鋪置代碼,測試人員測一下,誒沒有問題,好了。 推給用戶,Case 關掉,很簡單,對不對? 剛剛發生了什麼?(講者拿著空氣鏟子作勢挖來挖去) :satisfied: 這個坑又深了一點點,對不對?所以不能這樣做。 :::success 第一步,找到改動點(先別改,避免再度挖坑) ::: 找到了改動點之後,接下來呢,我要找到測試點。 :::success 第二步,找到測試點 ::: 遺留代碼很大,這些地方哪,都很難看。 我煩它們煩了很久了,我非常想把它們重構一下,不要這樣做,跟你沒關係。如果你改它們的話呢,你是自找麻煩。 :::warning _我非常想重構!! (千萬別改它,跟你沒關係)_ ::: 那這段代碼呢,你總是要改嘛,對不對?你找到了那個改動點。 然後呢,第三步,把它斷開依賴。 :::success 第三步,斷開依賴 ::: 第四步呢,給它們加些測試。 :::success 第四步,寫測試 ::: 這些測試是根據你對代碼的理解,對業務的理解加上反向工程的測試。這些測試說實話意義不大,因為其實並沒有新知識產生,對吧? 它只是代表了你當前你對這個代碼的理解。 第五步,再寫一個測試。 :::success 第五步,修改代碼並重構 (先寫測試) ::: 這個測試跟前面的測試不太一樣。為什麼不一樣呢? 這個測試你寫完了之後,它會失敗,為什麼它會失敗? 有 bug 嘛,對不對?也驗證了一下這個 bug 還在的。 然後呢,把 bug 改掉,重構,誒再提交代碼,關掉 Case。 這樣呢,整個世界剛剛變得美好了那麼一點點,對吧?這個坑沒有變得更深。 也許你一直這樣做下去,世界會變得更美好。 這聽起來蠻簡單的,對不對? > 照著做下去 世界會更美好 > 聽起來很簡單是吧 > [name=講者][color=#90b7f7] 很簡單嘛! 這裡面 1,2,3,4,5 它的哪一步最有挑戰? 第 3 步! ![](https://i.imgur.com/kj3Bqdv.png) 你看 ??? 都是有經驗的人。 第三步,因為啊,當初寫代碼的這位仁兄啊,他從來沒想過,有朝一日有人會拿他的代碼來做單元測試。所以代碼沒有可測性。對吧? :::success 斷開依賴最有挑戰性 ($\because$ 不具可測性) ::: 這個這個,你想要測的只是函式當中的一小段,但是為了要測這個函式,你總要動這邊一下嘛,對吧? 你看,哎呦,構建函數裡面有十個參數,這十個參數還分別都是對象,每一個人都還有自己的十個參數。:satisfied: 那你的測試寫出來的時候啊,差不多整個網站都起來了。:satisfied: 所以說,斷依賴就是最重要的一個技術。 :::success 斷依賴是最重要的一個技術 ::: 很遺憾在這裡,我只能說一些比較虛、比較空的東西。 具體的技術大家要自己去看書齁。 :::success 遺留代碼演算法: 1. 找到改動點 - 不要急著改 (避免再度挖坑) 2. 找到測試點 - 不要急著重構,而且與本次的關注點沒有關係 3. 斷開相依性 (依賴) - 最有挑戰 ($\because$ 撰寫程式碼時沒有想到可測性) - 最為重要 ($\because$ 要寫單元測試,發現相依性太高 $\implies$ 變成整個網站都要寫……) 4. 寫測試 - 理解業務 + 反向工程 → 測試 - 代表對代碼的理解 5. 做出變更與重構 - 寫測試 → 第一次一定會失敗 (驗證 bug 存在) - 改動並重構 → 測試通過 - 發佈給使用者 - close case ::: ### 自働化 (Autonomation) ≠ 自動化 (Automation) ![](https://i.imgur.com/82Q03MC.png) 在這裡,關於測試還有一點我需要補充,就是關於自動化的問題。 左邊自働化跟右邊自動化有什麼區別啊? 這個字(働)在台灣有用嗎?帶一個人的這個「働」啊。 這是日文叫做 じどうか (Ji dō ka),這兩個日文都唸作 Ji dō ka。 這邊這個 Ji dō ka (自働化) 可能在日本用得更多,單獨拿出來呢就是 はたらく (Hataraku),它是人工作的意思。 左邊這個自働化,它可以翻譯成 Autonomation,Autonomation 是什麼意思呢?它是指我們的系統有這樣一個能力,它能夠檢測出異常,看我們之前的假設是不是被破壞了。 如果這個異常發生了,它檢測到了之後,它會讓整個生產過程停下來,這樣就不會繼續產生浪費,然後它會通知我們人,然後「人」過來理解這個問題,然後把問題改正以後呢,再對系統做一個改進。 這個就是 Autonomation。 :::success 自働化 (Autonomation) - 由豐田的新鄉重夫提出 - 系統具備異常檢測的能力 - 異常事件 → 停止生產 → 通知人 → 人來理解並改正問題 → 改進系統 ::: 大家想一想,我們的自動化測試,我們的持續集成 (CI) 其實都是從這裡來的。 而自動化呢,其實在日語裡面,這個「動」啊,其實是沒有人的因素在裡面的。就是「動」,機器在「動」的這樣一個動作。那個就是機器自己在「動」。 很遺憾,我們很多人,工程師啊,甚至於説有經驗的軟體工程師,花很大很大的精力在這第二個的自動化上面。用一個程式來自動的完成工作。 大家有沒有想一想,這個自動化的程式,它也是軟件,是吧? 那關於這些軟件的知識,你又把它放在哪裡了呢? 大家可能都有這樣的經驗,我們一開始有一個非常完美的自動化測試的工具,可是過了一段時間呢,沒辦法它太複雜了,沒人能夠理解它。就變得不可維護了。這東西就沒法用了。 :::warning 再好用的自動化工具,時間一久就沒有人「知道怎麼」維護了 ::: 所以說,我們真正需要的,是這邊的自働化。 這個觀點來自於豐田生產系統,這個人叫做新鄉重夫 (しんご しげお,Shingo Shigeo),他對這個東西影響也蠻大的。 我這裡沒有時間細細的介紹,如果大家有興趣的話,可以去找這個人,或找這個概念。對我們工作是蠻重要的。 這個東西(自動化)產生遺留代碼。 這個東西(自働化)固化我們的知識。 這是他們的區別 :::success 自動化 → 產生遺留代碼 自働化 → 固化我們的知識 ::: ## 從「軟體架構」的角度來談 Legacy Code ![](https://i.imgur.com/jjtVc2v.png) 第二個角度,關於架構。 誒,我平常不是很有機會在大庭廣眾之下,講一講架構這個事情。 說實話,真是沒什麼內容。所以我今天啊,是想把這個部分給跳過去。 關於架構呢,我有幾句話想說。 首先啊,我覺得談到架構,就不得不談到這個,對我們做軟件的人來講,最最重要的指導原則。就是高內聚、低耦合。 這個對台灣來講也是一樣的吧?高內聚、低耦合。 很重要,有多重要? 我們早晚有一天會死,對不對? 那麼在你死之前,你的孫子或者是曾孫在你的病榻旁邊,你要留給他一句話,你要跟他說什麼? 高內聚、低耦合 :skull: :angel: 然後再死,就是這麼重要。:satisfied: :::success 「高內聚、低耦合」具有必須一代傳一代的重要性。 ::: 雖然這麼重要,這兩件事情的優先級不一樣。 哪一個更重要?哪一個優先級更高一點? 說哪一個更重要可能不準確,哪一個優先級更高一點?大家覺得呢? ### 提高內聚需優先做到 ![](https://i.imgur.com/hBsPwkq.png) 我的老師,Craig Larman,他給了我很多方面的指教,他不斷的跟我說,高內聚優先級更高一點。 :::success _高內聚較優先_ ::: 為什麼呢?我說了那麼多高內聚,它究竟是什麼意思呢? ![](https://i.imgur.com/yPZTwJr.png) 指的就是啊,這兩個東西應該在一起的話,我就應該把它放在一起。 :::success 高內聚:就像是剪刀,應該在一起就不應該拆成兩半 ::: 比方說剪刀,英文叫做 scissors,對吧? 你不能總拿著一個 scissor,然後問說另外一個 scissor 在哪裡?對吧?這用起來太麻煩了。 ![](https://i.imgur.com/wt2w3qv.png) ???來講,如果你在某一刻你做了一個決定,你決定我的系統暫時可以沒有高內聚,借了技術債,對吧? 那麼以後如果要還的話,還的這個成本跟我今天把它做成高內聚,會是一樣的嗎? 現在啊,你只有這兩片剪刀,對吧? 我一看,這片在我手裡拿著,另一片在那邊,對吧? 當你東西越來越多的時候,還好找嗎? 就會變得越來越難找。 所以說呢,如果你欠了這種債的話,你要還的利息就像那種複利一樣,它是利滾利,越來越貴,越來越貴。 所以,如果你沒有高內聚的話,最好馬上把它做出來。 ### 降低耦合可以不用急 另外一個角度叫做低耦合,低耦合就是軟件模塊之間,互相知道多少,我們希望互相知道越少越好。 這把瑞士刀,大家覺得怎麼樣? ![](https://i.imgur.com/EWwwaNb.png) 我覺得蠻酷的,對吧?我也想要有一個。 而且說實話,如果我一個人用它的話,有問題嗎? 不太好用這是真的,不過這挺酷的,對不對? 這不是太大的問題。 ![](https://i.imgur.com/QhImDHY.png) 那麼我沒有低耦合的話,現在我不是一個人工作了,有一個人跟我一起工作,我要用刀子的時候,他需要用個鋸子,麻煩了,是吧? :::success 沒有低耦合的話,就會像幾十合一的瑞士刀一樣 功能應有盡有,只是用不順手 一個人使用還可以,但多人使用時很難用 ::: 我如果沒有低耦合,現在我把它解開耦合,那麼這個成本跟我當時解耦合有區別嗎? 恐怕不會很大,因為問題本身就是它太耦合了,所以解耦合不要那麼急。 :::success 解耦合可以不用那麼急 ::: 可是大家回頭想一想,作為一個初級的軟件工程師,他一開始只知道怎麼把軟件做出來而已,對不對? 他不懂什麼叫高內聚低耦合,當他逐漸開始獲取經驗,開始看書的時候,會讓他眼前一亮的,往往是那些解耦合的手段,設計模式啊,設計原則啊,這些東西。 然後他就開始瘋狂的解耦合,雖然他不知道為什麼。:satisfied: :::danger 常犯的毛病:不明所以的解耦合 ::: 當他把這些東西都學會了之後呢,也要換一家公司了。:satisfied: 是吧? Legacy Code 就是這麼來的。 > 讓軟體工程師眼睛為之一亮的,就是 design pattern 等解耦合的手段,他就會開始瘋狂的不明所以的解耦合,然後就換公司了。 > Legacy Code 就是這樣來的。 > [name=講者][color=#90b7f7] 為什麼要說這些呢?其實是想提一提微服務的事情。 ![](https://i.imgur.com/Pn6G4C5.png) 微服務在這裡蠻流行的,說實話昨天有一位講師問,在場有幾位 C++ 工程師,只有一位舉手,其實是我本人啦。 然後在另一個場地呢,有人問,有多少人想做微服務,差不多所有人都舉手了。這個偏差還蠻大的。 :::info 重構成本 隨著時間推移,提升內聚困難,降低耦合相對容易 ::: 微服務說起來,就是一個解耦合手段。對不對? 它就是一個解耦合手段。 :::success 微服務是個解耦合手段 ::: 你急著解它幹什麼呢? 你連單一的軟件都做不好,那麼你解它來幹嘛呢? > 解耦合你能解出 feature 嗎? > 是解不出來的,對不對? > [name=講者][color=#90b7f7] 包括微服務的提倡者 Martin Fowler,這是直接從他的 Blog 裡面截來的,那麼他也是不支持大家從一開始就採用微服務這樣一個方式。 ![](https://i.imgur.com/vcTtNcY.png) Picture from: https://www.martinfowler.com/microservices/ OK,那這些是關於架構。 ## 從「組織架構」的角度來談 Legacy Code ![](https://i.imgur.com/B8XmqS8.png) 第三個部分可能是我個人更感興趣的一個部分,就是 Legacy Code 和我們組織結構、組織行為之間的關係。 ![](https://i.imgur.com/VcqLGpn.png) ㄜ 有兩個方面,在我介紹之前呢,先介紹兩本書,相信大家都知道。 第一本書叫做 "The Fifth Discipline" by Peter Senge,我們翻譯叫做第五項修煉,應該差不多,我想。 原文書:[The Fifth Discipline: The Art and Practice of the Learning Organization](http://www.books.com.tw/products/F010669763) by Peter M. Senge, 9781407060002 中譯本:[第五項修練:學習型組織的藝術與實務 (全新增訂版)](https://www.tenlong.com.tw/products/4713510945223) - 彼得.聖吉 著;齊若蘭, 郭進隆 譯;4713510947 ![](https://i.imgur.com/wQkIYyi.png =144x220) 那麼在這本書裡面呢,Peter Senge 給我們介紹了系統思維 (Systems Thinking),那麼他講到啊 Global optimization 和 local optimization 他們兩個不是一回事,而且往往恰恰相反。 :::success **局部優化往往對於全局優化是有危害的** ::: 另外一本書呢,更新一點,叫做 "Thinking, Fast and Slow",作者叫做 Daniel Kahneman。 原文書:[THINKING, FAST and SLOW](http://www.books.com.tw/products/F012739974) by Daniel Kahneman, 9781846140556 中譯本:[快思慢想 (新版)](https://www.tenlong.com.tw/products/4713510945049) - 康納曼 著;洪蘭 譯;4713510947 ![](https://i.imgur.com/BEnW6VF.png =144x220) 這本書是基於他大概三十幾年的一個研究經驗所寫成的。 他跟我們講就是說,我們人啊,有兩種思維方式,一種是快思維,一種是慢思維。 快思維呢是基於我們的 instinct 和這個這個 emotion 來做的這樣一個決定。 慢思維呢是更理性的,有邏輯性的這樣一個思維。 那麼我們人在進化過程當中啊,面臨的主要挑戰是生存。 那麼對於生存來講,最重要的是局部優化,對不對? 我要迅速地做出一個局部最優的,這個這個解決方案來,保證我能生存下來。對吧?至於全局優化是以後的事情。 所以說我們快思維所產生的這些想法,往往都是局部優化。 :::success **想太快往往都只有局部優化而已** ::: 但很遺憾的是,在我們決定怎麼來設計一個組織的時候,往往都是拍拍腦袋而已,這裡沒有太多的理性。甚至說很大的公司都是這個樣子。 那麼我想介紹兩方面,第一個方面叫做 Conway's Law。 ### [康威定律 (Conway's Law)](https://en.wikipedia.org/wiki/Conway%27s_law) ![](https://i.imgur.com/F3qOum5.png) Conway's Law 相信大家都聽說過吧? Conway's Law 也蠻早了,這個人叫做 Melvin Conway,他在很早以前 (1968) 寫了一篇[論文](http://www.melconway.com/Home/Committees_Paper.html),他介紹了他的一個觀察,就是說,我們軟件開發組織跟我們軟件架構,它們之間會有一種對應關係。 ![](https://meekrosoft.files.wordpress.com/2013/06/conway.png =138x227) > Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. > [name=Conway's Law][color=#90b7f7] :::success 軟體架構與組織架構會有某種對應關係 ::: 如果我有這樣一個組件,可能就有這樣一個團隊或是一個部門來跟他相對應。 如果我在做一個編譯器的話,那麼如果我有四個部門在一起協作的話,那很有可能所寫出來的編譯器就分成四個步驟。 預編譯,然後怎麼樣 怎麼樣 怎麼樣,這樣四個步驟。 那 Conway's Law,如果你老老實實把 Conway 所謂的 Law (就是法則) 當作一個法則來運用的話,那麼你的團隊,你的組織結構就會這樣。 ![](https://i.imgur.com/d8Oigk9.png) 你有一個 component A,然後你就會有一個 team A 或 A 部門來對應它。 比方說,我有一個前端,那我就有一個前端團隊來對應它。 我有一個通信模塊,那麼我就有一個通信模塊團隊來對應它。 那這樣呢,你就等於是在遵循 Conway's Law。 Conway's Law 雖然只是一個觀察,但是你把它當作是一個法則來遵守了。 這樣一來呢,就會產生一個這樣的效果。 潛意識裡你在期望什麼呢?你在期望啊,通過我的組織,通過我組織裡的這些人來 preserve 關於這個產品 component 的知識。 對吧?因為這些人,他們會精於這個 component。 那麼重點就是說,一開始你就製造了一個遺留代碼的溫床。 這東西,這知識,你從一開始就沒指望,它是通過 Autonomation 來保存的。而是通過人來保存的。 :::warning 一個團隊負責系統中的一個模組,潛意識是想透過組織裡的**人**來保存知識,從一開始你就製造了一個遺留代碼的溫床(應透過自働化) ::: 也可能事情恰恰相反,正是因為你有了 Legacy Code,所以你被綁架了,你不得不採用這樣一個組織方式。 所以這樣來組織的話,自然而然的你會得到 Legacy Code。 而且呢,這種組織方式導致了局部優化,那麼大家會在一個 component 的範圍裡面去優化,最後產生了更多的遺留代碼。 而這是一個方面,所以大家在考慮組織結構的時候,不要去遵守 Conway's Law。它只是一個觀察而已。 :::danger 組織團隊時,不要遵守康威定律!它只是一個觀察而已! ::: 另外一個角度,前面是第一個角度,第二個角度我要來介紹一個新概念,叫做 Representational Gap。就是這個表現差,表現差 (註:可不是指表現差勁)。 ### Representational Gap (表現差) ![](https://i.imgur.com/n2K7zhm.png) 左邊這張圖,大家知道他們在幹嘛嗎? 這個叫做 Code Review。:satisfied: 那個時候啊,代碼還要打在這個紙帶上面,然後這個時候啊,Representational Gap 很大,對吧? 就是說,我的這個實現方法,跟我要解決的問題,它們之間的差距太大,那麼這樣的維護成本就會非常高。 :::info 表現差:_實現方法_ 跟 _要解決問題_ 的差距 ::: 你怎麼去維護這種代碼,恐怕這個代碼寫好之後就沒指望有人維護,對不對? 這個嘛,有點超現實,這個是 Avatar 那部電影裡面截的一個圖。 ![](https://i.imgur.com/IjFNVPH.jpg) 這種它的表現差就會很小,很小。 表現差小,我們對理解業務就更加的便利,我們所有的精力都放在業務本身上面,不需要花太多的精力在實現上面。 ![](https://i.imgur.com/vVi3K7Z.png) 那麼,如果說我們的業務領域跟解決方案領域,它們之間有一個比較好的對應的話,這種代碼比較好維護。 為什麼呢?因為將來當我的業務發生變化的時候,我可以相應的在解決方案這邊做變化,而不至於說業務這邊一個小小的變化,導致我解決方案這邊一個很大的變化。 所以說,如果沒有這種對應的話,這種代碼就是說,你的知識沒有很好的 preserve 下來。那麼將來會很難維護。 我們觀察發現,剛才大家看到這樣一個團隊組織方式,我們把它叫做 component team,這樣的一個組織方式。 然後,team 和 component 之間有一一對應關係,然後後面呢,通過這塊叫什麼? 這塊叫什麼?項目管理。這就是項目管理。 你通過項目管理,然後來達到目的。 這是一種組織方式。 然後我們觀察發現呢,如果你的團隊組織方式是這樣的,你是圍繞著用戶價值來組織團隊的。 那麼在我們做的這個,這邊是所謂的 Product Account,就是我們要做的 ???? 中心的內容,那它們跟團隊之間是有一個簡單的對應關係的,也就是說沒有,沒有項目管理了,但是呢我們所有的團隊都可以去改所有的代碼。 以這樣的一種方式,也就是代碼共有的方式來組織團隊。 誒我們發現在這種情況下,更加容易產生那種,首先全局優化的決策。另外呢,它會產生更好的這個,business domain 和 solution domain 之間的對應關係。 :::success 用 _程式碼共有_ 的方式組織團隊 → 促進全局優化的決策 → 促成業務領域與解決方案領域間更好的對應關係 ::: 所以說,這種組織方式,我們把它叫做 Feature Team。 Feature Team 是以用戶價值為中心來組織團隊。 這種組織方式顯然強迫你把你的知識 preserve 在 Autonomation 裡面。 自働化裡面,而不能只指望某一個人。 :::success **Feature team** 以用戶價值來組織團隊 強制知識保存在自働化裡,而非指望著「人」。 ::: 其實指望著人呢,只是一種假象。 你的組織結構呢,是一個很抽象的東西,裡面要填滿人才有意義。對不對? 那人是會流動的。 我剛才提到了,如果你想要這樣組織團隊的話,有一個大前提是要做到代碼共有。 :::success Feature team 組織方式的大前提是 **代碼共有** ::: _代碼共有_ 聽起來簡單,對不對? 剛剛我說的時候,好像 好像聽到了幾聲冷笑。 那現在更多了。 沒那麼簡單,對吧? :::success _代碼共有_ 沒這麼簡單 ::: ![](https://i.imgur.com/9opXxRU.png) 所以說,你要想做到代碼共有的話呢,這東西來得不容易,也不便宜。 你需要有很多 supporting 的 practice。 這些不是 best practice,但是呢,往往這些 practice 如果你沒有的話,你去做代碼共有,最後只會死得很慘。 比方說,測試驅動開發。 測試驅動開發幫你獲取知識的同時,你的知識就已經被 preserve 下來了。對吧? 然後呢,pair programming,現在更流行的是叫做 mob programming。 我們推廣知識,不斷的重構,保持代碼整潔,讓 solution domain 不斷的向 business domain 靠近。 然後持續的集成。 我們講持續集成和持續集成系統是完全不同的兩個概念。 持續集成系統是你的 Jenkins,那麼持續集成是你真的持續的在集成。 Autonomation :::success 實現代碼共有至少需要下列實務: > 不是最佳實務,但沒有這些實務會死得很慘 > [name=講者][color=#90b7f7] - TDD (測試驅動開發) - pair (mob) programming - 重構 - CI - Autonomation ::: 沒有這些東西,空談代碼共有恐怕不現實。 ![](https://i.imgur.com/0o5ZlT6.png) 我要講的就這麼多,最後我想感謝邀請我來的 TitanSoft (鈦坦科技),還有我的同事 91 (讀作久邀) 陳仕傑。 因為我知道,可能會用中文來講,跟他討教了內容還有關於怎麼用詞上面的一些問題,希望我用得準。謝謝。:clap: :clap: :clap: :clap: :clap: ## References: - [Compoent Team vs Feature Team](https://less.works/less/structure/feature-teams.html) ![Compotent Team vs Feature Team](https://less.works/img/structure/component-vs-feature-teams.png.pagespeed.ce.NKNnxhQVFQ.png) ###### tags: `MW18`