l
顯示具有 agile 標籤的文章。 顯示所有文章
顯示具有 agile 標籤的文章。 顯示所有文章

2023年3月15日 星期三

使用ezSpec落實行為驅動開發與實例化需求(2):Feature與Story

March 15 08:57~11:00;20:02~08:19

▲用ezSpec寫ezSpec的使用說明文件


前言

上一集<使用ezSpec落實行為驅動開發與實例化需求(1):領域模型介紹>提到Feature是Gherkin用來描述需求的最大單位,今天介紹在ezSpec中如何撰寫Feature。

在介紹撰寫Feature之前,先說明External DSL(外部領域特定語言)Internal DSL(內部領域特定語言)這兩個名詞。使用Cucumber、SpecFlow或Cucumber-JVM這類工具的鄉民,將Feature寫在feature file裡面,再交由工具產生Step Definition,然後撰寫Step Definition以達到驗收測試自動化的目的。Gherkin是一種用來描述需求或規格的DSL(Domain-Specific Language,領域特定語言),Cucumber系列工具的作法Teddy將其稱為External DSL,因為用來描述需求的語言(也就是Gherkin),和開發用的程式語言(Ruby、C#或Java)並不相同,因此Gherkin成為「外部領域特定語言」。

External DSL的好處與缺點Teddy在<無痛將驗收測試文件寫在測試案例中>與<使用ezSpec落實行為驅動開發與實例化需求(1):領域模型介紹>提過,有興趣的鄉民可參考。ezSpec是一種Internal DSL,使用Java語言模擬Gherkin,換句話說用來描述需求或規格的DSL與開發者使用的程式語言是一致的。

***

撰寫第一個Feature

ezSpec除了採用Internal DSL的方式開發,它也與JUnit 5結合,撰寫Feature檔就和傳統寫一個Test Class是一樣的方式。請參考圖1第8行,FeatureSpec是一個一般的Java Class,名稱可以隨便取,你也可以把它叫做FeatureTest或任何你喜歡的名字。

FeatureSpec實作ezSpec interface,這麼做可以讓ezSpec自動產生報表。如果你只需要在IDE裡面觀看JUnit所產生的報表,也可以不用實作這個介面。

在第9行宣告一個static Feature feature 物件,用來代表一個feature file。請注意,如果你要讓ezSpec自動產生報表,這個static Feature的變數名稱一定要取名為 feature,否則系統無法自動產生報表。接著第11~21行使用JUnit 5標準的@BeforeAll annotation來定義feature的內容。第13行的Feature.New()產生一個新的Feature物件,它接受兩個參數:

  • Name:Feature的名稱,用來代表一個交付給使用者的功能或功能組。
  • Description:Feature的詳細內容描述,除了進一步說明Feature的內容,也可以拿來定義這個Feature會用到的詞彙,這些詞彙可以成為通用語言(ubiquitous language)的一部分。

 

▲圖1:ezSpec 的Feature使用範例

 

定義好feature之後,可以直接寫一個test method來執行看看,驗證feature的內容,請參考第24~40行,這個test method單純驗證feature的內容是否正確,其產生的報表如圖2。

 

▲圖2:ezSpec產生的Feature報表範例

 

***

Story

ezSpec 的Feature物件本身只是一個「容器」,實際上的需求內容是寫在Scenario裡面。在Feature與Scenario之間,ezSpec還支援Story。一個Feature需要包含至少一個Story,Scenario則是附屬於某一個Story。

請參考圖3,第16行呼叫feature.newStory新增一個Story,它和Feature一樣有Name和Description欄位,其用途也類似。此外,Story還有一個Index欄位,可以指定一個編號給它,之後可以使用這個編號來讀取Story。

基本上Story就是一個小Feature,如果你覺得不需要Story來管理更小的功能,在撰寫Feature的時候,只需要幫每一個Feature寫一個Story,然後由該Story來產生所有的Scenario。

 

▲圖3:ezSpec 的Story使用範例

 

▲圖4:ezSpec 產生的Story報表 (未包含內部的Scenario、Scenario Outline與Background)

 

新增Story之後,可以呼叫feature.withStory拿到屬於該feature的story,然後再透過它的newScenario產生Scenario。下一集將介紹如何使用Scenario與Given-Then-Then撰寫需求範例。

***

友藏內心獨白:運動前先暖身。

2022年2月11日 星期五

愛上Mob Programming之突破瓶頸篇

Jan. 11 03:22~04:55

▲圖1:Pitest產生的涵蓋率報表

 

前言

今年初開始幫ezKanban的use case tests(使用案例的測試案例)改用Given-When-Then的格式讓它更接近Living Documentation,如圖2。改完之後發現use case tests與Aggregate Root測試案例(單元測試)有許多重疊的現象。因為ezKanban的所有Aggregate Root都包含了合約(Contracts),在程式執行期間會自動驗證程式正確性,因此Teddy就在想「是不是可以透過Specification by Example方式所撰寫的驗收案例,加上幫Aggregate撰寫合約,來省略entity layer的單元測試」。


▲圖2:加上Given-When-Then的測試案例

 

如果這個想法成立,就可以在確保程式正確性的前提之下少寫很多單元測試 。問題是怎麼知道拿掉entity layer的單元測試只要有Aggregate Root的合約依然可以確保程式正確性呢?軟體測試中有一種叫做Mutation testing(變異測試)的方法可以回答這個問題。

 

***

驗證關卡1

有兩位非常優秀的ezKanban的成員負責驗證這個想法,這個題目並成為其中一位的碩士論文。他們找到Pitest這個Mutation testing工具,但是使用在ezKanban的時候遇到問題一直無法解決。因為這是個最新冒出來的題目,所以還沒有機會在ezKanban團隊的mobbing活動中一起處理,這兩位成員是利用mobbing以外的時間去嘗試解決。

昨天和ezKanban團隊mobbing,上午把這幾周以來一直在處裡的持續整合工作告一段落。盤點一下手邊的工作,團隊決定一起看看Pitest的問題。團隊手邊有一個測試用的小專案,可以正常執行Pitest,但是當團隊在ezKanban的專案中執行Pitest卻會出現如圖3的不明錯誤訊息。


▲圖3:Pitest錯誤不明訊息

 

由於團隊是透過maven去執行Pitest,因此大家懷疑是不是maven專案的pom.xml檔案設定有問題。於是團隊試著修改pom.xml檔案但問題還是沒解決。後來Teddy想到,既然有一個可以正常執行的專案,那乾脆把ezKanban的程式碼複製到這個專案中看看能不能執行,這樣子就可以確定到底是ezKanban程式碼導致Pitest執行失敗,還是因為maven設定的問題。

將ezKanban程式碼複製過去之後可以正常執行Pitest,於是團隊試著比較兩個專案的pom.xml的差異。大家看來看去也看不出來到底是哪裡有問題,索性將兩個檔案文字比對,如圖四)。

 


▲圖4:比對兩個專案的pom.xml差異

 

發現原來ezKanban使用JUnit 5的artifactId是junit-jupiter-api,如下:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
</dependency>

 

而Pitest只支援JUnit 4,但有鄉民幫它加工之後讓它可以在Junit 5執行,但是此時artifactId要改成junit-jupiter。

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>

 

改用正確的依賴之後,Pitest就可以產生如圖1的報表。

***

 

驗證關卡2

ezKanban系統由若干的maven專案所構成,一開始團隊使用DDDCore這個相依性最簡單,只包含Clean Architecture的Entities Layer與Use Cases Layer,沒有使用到SpringBoot與資料庫的專案來測試Pitest。測試成功之後改用Account專案,這個專案除了包含Clean Architecture中的完整四個Layers,除了使用到SpringBoot與資料庫,還用到Java 17最新的預覽功能,例如Pattern Matching for Switch。

執行Pitest之後出現圖5所示的錯誤訊息:


▲圖5:Pitest無法辨認byte code格式

 

很簡單啊,就是執行Pitest的時候加上—enable-preview參數就好了啊。問題是,要加在哪裡?試了幾種方式,後來有團隊成員找到正確的格式,如圖6所示。


▲圖6:加上—enable-preview參數讓Pittest支援Java 17預覽功能

 

***

打破個人的瓶頸

瓶頸就是系統中生產力最弱的環節,每一個人都有自己擅長的地方,也有自己鬼遮眼的時候。軟體開發是一個動態的系統,採用傳統的單人開發模式(solo programming),每位開發人員同時各自開工,看似生產效率很高,但事實上很可能這些各自執行的thread經常處於block(卡住)狀態。

Pair programming可以稍微改善這種情況,但兩個人一組還是比不上全部的人一組。你可能會說:「全部的人一組還是會有盲點啊,可能這個鬼很厲害,把全部的人的眼睛都遮住了。」沒錯,可能會這樣。但從Boundary(邊界)的角度來看,整個團隊一起開發已經是這個團隊能力範圍內最大的「邊界」,也就是他們已經同時間盡其所能的一起合作解決問題。如果這個「鬼」那麼厲害能夠遮住全部人的眼睛,你派一個人或是兩個人去對付這個鬼,更加無法打敗它。

 

***

 

友藏內心獨白:團隊一起處裡有價值的工作。

2021年9月15日 星期三

成本與價值

September 15 18:36~19:02

▲ezKanban 團隊mobbing實況


學員:如何讓整個團隊,包含剛進來的新人,都可以學會用DDD(領域驅動設計)開發軟體?

Teddy:我有一個很簡單且有效的方法,但這個方法就算你知道了你們公司應該也不會採用。

學員:什麼方法?

Teddy:Mobbing,又稱為Mob Programming,整個團隊,包含測試、UI/UX與Product Owner,一起開發軟體。

學員:類似Pair Programming嗎?

Teddy:對,不過是Pair Programming的加強版,不只是兩個人一起開發,而是整個團隊。

學員:這樣的確是很難在公司推行,Pair Programming老闆都覺得成本太高

Teddy:那你們都怎麼帶新人?

學員:我們會指派一位導師帶著新人做一陣子。

Teddy:然後呢?是不是一、兩個禮拜之後就讓新人單飛,然後每天問他進度?

學員:(苦笑)沒有這麼慘啦,有問題還是會回答。

Teddy:軟體開發的成本,其實很難量化。老闆通常只看到「開發人員」的成本,因為這是最直接的成本。但除了直接寫code以外,其他的成本呢?

Teddy:你要不要Code Review?有bug要不要改?要改多久?做出來的東西客戶不要怎麼辦?舊員工離職新員工交接怎麼辦?軟體搞到變成硬體,改不動也沒人敢改怎麼辦?後端、前端、內人、外人互相等對方工作完成怎麼辦?需求不清楚開發人員亂寫怎麼辦?測試團隊自創需求,胡亂回報issues怎麼辦?

Teddy:從精實開發的角度來看,這些都是浪費。很多的等待(延遲)、重工、半成品、過度生產、交接、缺陷、工作切換、重複學習。我不敢說Mobbing可以完全消除這七種浪費,但以我的經驗,這種「看起來成本太高」的開發模式反而能夠消除浪費而降低成本並提高產品的品質與價值。

Teddy:老闆覺得開發成本太高,會不會是因為自身的產品在市場上的價值太低,或是想要犧牲品質來拉低成本,所以任何提高品質的活動都會被認為是增加成本。不管如何,這有可能是對於自己產品的定位不同,產生的價值觀落差。

***

沒有銀子彈,但是有高科技武器跟二戰時的武器之分。

***

友藏內心獨白:知道了也做不到。

2021年1月22日 星期五

愛上Mob Programming(下)

Jan. 21 18:22~19:50

▲喵星人也要一起mobbing


前言

乍看之下,mobbing是一種浪費,整個團隊一起開發卻只用一台電腦寫程式,感覺是把原本可以平行同時工作變成只能循序工作。今天Teddy從敏捷與精實開發的角度來看,為什麼值得做mobbing。

首先,所有的開發工作,不管是多少人一起開發,大致可以分成兩種工作模式:

  • Component Development:開發人員專注於開發軟體系統的某個元件,例如,寫後端領域邏輯、設計資料庫schema與下SQL、寫前端JavaScript、寫CSS。這種模式底下開發人員所完成的工作並不能直接使用,需要經過另一個人整合之後,例如串起前後端,才會變成一個完整的功能。傳統的瀑布式開發,大多採用這種專業分工的模式。
  • End-to-End Development:又稱為feature development,開發人員需要負責整個功能,也就是所謂的全端開發。全端開發工作並不限定一個人的全端,也可以是交由一個團隊來完成。例如Scrum團隊就是一個可以交付功能的跨職能團隊(全端團隊)。

***

單人開發

單人開發是一般人最熟悉的開發模式,又稱為Solo Programming。如果是採用component development的單人開發,所完成的工作並不能直接交付給使用者,也就是無法直接產生價值,還需要靠整合流程將所有的元件合併成可使用的功能。

從精實開發的角度來看,這種模式所完成的工作都是半成品(work in process/progress;WIP),做得越多,只是累積更多庫存,徒增浪費

如果是feature development的單人開發,這個人就是所謂一條龍全端工程師,可以獨立產生價值。好的全端工程師非常難得,可以做到一個人的end-to-end。但人的能力總有侷限,有些全端工程師是屬於比較偏後端的全端工程師,有些則是前端比較強的全端工程師,也有那種前後端都不熟的全端工程師XD。

在這種情況下,全端工程師的產出,就受限於他自己最弱的一環。假設Teddy的後端能力有100分,前端能力只有20分,Teddy的總產能,不是 (100 + 20) / 2 = 60分,而是只有20分。這就是瓶頸,流程中生產力最弱的那個環節將會決定整個系統的輸出

此外,單人開發還會造成維護的問題。如果這個人離職了,他所負責的程式通常連他自己都看不太懂了,更何況要找一個人來交接工作,難上加難,錯誤百出。

***

雙人編程

為了解決單人開發的問題,eXtreme Programming(XP)很早就鼓吹雙人編程或稱為結隊編程的pair programming。兩個人一起開發,一位扮演司機,另一位扮演領航員,兩人彼此互補,兩顆腦袋一起使用,可以減緩單人開發所遭遇到瓶頸的問題。假設採用feature development:

  • 小明:後端能力90分,前端20分,系統產出20分。
  • 小英:後端能力40分,前端60分,系統產出40分。

兩個人一起合作,整個系統的產出是60分。看到這邊你可能會覺得Teddy在莊孝維。小明、小英個別開發,20 + 40 也是 60分啊,而且搞不好個別開發速度更快。

但實際上,個別開發的速度,除了受限於每個人自身的能力,還受到很多因素影響。例如,專注力、解決bug的能力、當下看出設計問題的能力等等。一個人開發經常會陷入自己的盲點,導致被一些很簡單的「低級bug」給困住跑不出來,徒增很多試錯的時間。

雙人編程可以減少上述問題的發生,又可以自然產生一種良好的「備胎效益」—所有的程式碼至少有兩個人以上都有能力開發與維護,萬一人員異動也不至於整個停擺。

但雙人編程也會造成其他問題,例如如何搭配成員。把兩個菜鳥搭配在一起顯然無法減少瓶頸所造成的限制生產力問題,把兩個高手高高手安排在一起,可能大部分的時間都在吵架「華山論劍」,樂於忙著練功卻忘了實際的開發進度。

***

團隊編程

在mob programming的情況下,團隊成員每個人同時間都貢獻他自己最強的能力在同一件事情上面。例如團隊有五個人:

  • 小明:UI/UX擔當。
  • 小華:JavaScript高手。
  • 小英:後端專家。
  • 小龍:新人。
  • 小陳:Product Owner(PO)。

在mobbing的情況下,這五個人一起開發。假設現在要開發ezKanban的使用者註冊功能,如下圖。


▲ezKanban使用者註冊流程


如果是跑Scrum,PO需要先寫好user story,然後在sprint planning meeting帶過來跟團隊解釋。此時團隊可能選擇估算每一個user story的大小,接著再將user story切成tasks。

Spring planning結束後,產出spring backlog並將其布置成task board。然後每天Daily Scrum的時候團隊在task board前面報告三件事並認領新的工作。Sprint結束後還有review與retrospective活動,

如果改用mobbing,因為整個團隊一起開發,所以也就不需要另外再開會。要開發註冊功能,就直接從event storming的圖中選出Register User這個使用案例,如果開發人員對需求有問題,例如:

  • 要如何註冊?在ezScrum保存一份使用者註冊資料,還是結合Facebook、Google、Line的帳號即可?
  • 要不要整合LDAP做到企業內部單一登入?
  • 註冊畫面需要填寫那些資料?密碼的長度有限制嗎?

以往這些問題可能會在spring planning討論,如果沒有討論到,例如要不要支援LDAP,等到實作的時候,開發團隊就必須要再去詢問PO。但PO不一定可以立即被找到,因此就產生所謂等待浪費。也有可能開發人員自作主張,覺得應該要做到很有彈性,所以在沒有詢問PO的情況下自己加入支援LDAP的功能,導致過度設計,等review的時候才發現這個問題。過度設計,或過度生產,也是一種浪費。

在mobbing的情況下,有任何需求面的問題,當下就可以和PO討論,減少大量部必要的浪費。

在開發整個註冊功能的end-to-end過程中,有時候處理UI/UX、有時候處理後端、有時候處理前端、有時候釐清需求,團隊中每一個對該領域最擅長的人都可以貢獻自己全部的力量。當遇到自己不熟的領域,也可以從別人身上學習,逐步加強自己的能力。

至於新人,加入團隊當下就可以有貢獻。因為mobbing的時候大家輪流當司機(拿鍵盤與滑鼠的那個仁),就算自己不知道怎麼寫,導航員也會告訴你怎麼做。這種在工作中學習的模式,可以達到最快上手的目的。

***

單件流

Mobbing可以做到精實開發提到理想工廠單件流的效果,在生產線上,每個節拍時間就有一個工作在每個工作站中流動。不需要庫存,也沒有浪費(假設沒有缺陷XD)。Mobbing與生產線單件流的差別在於,mobbing是整個團隊跟著工作跑,當工作處於開發後端階段,整個團隊就開發後端;處於開發前端階段,就一起開發前端,只有一件工作在每個工作站流動。

Teddy以一個會寫程式的PO的角色,和ezKanban團隊mobbing近七個月,這段時間沒開過以往Scrum裡面的任何會議,但卻比以往任何時間更了解產品開發的進度與品質。

開發軟體,應該和打籃球、打橄欖球一樣,整個團隊同一時間一起在同一個球場打球。現在想一想,這不是再自然也不過的事嘛!

***

友藏內心獨白:用過就知道,說破嘴也沒用。

2021年1月21日 星期四

愛上Mob Programming(上)

Jan. 21 01:08~14:24

▲ezKanban團隊mobbing實況


緣起

約略四年多前Teddy聽指導教授鄭老師提到實驗室開始全面採用mob programming(簡稱mobbing,這是Teddy第一次聽到這個名詞。

如果鄉民們把mob programming丟到Google翻譯,會得到暴民編程的中文解釋,因為mob的英文就是暴民的意思。最近很流行的新聞—美國擁護川普的群眾衝入國會大廈,英語新聞就用mob來指稱這群人。

***

當然mob programming絕對不會是找一群暴民來寫程式,根據維基百科的解釋:

(譯自英文) Mob編程是一種軟件開發方法,其中整個團隊在同一時間,同一空間和同一台計算機上處理同一件事。這類似於結對編程,在結對編程中,兩個人坐在同一台計算機上,並同時在同一代碼上進行協作。通過mob編程,可以將協作擴展到團隊中的每個人,同時仍然使用一台計算機來編寫代碼並將其輸入到代碼庫中。

也就是整個團隊一起用一台電腦同時開發軟體。

Teddy第一次聽到這種作法,腦中的第一個反應是出現黑人問號的畫面。Pair programming很合理,但整個團隊綁在一起用一台電腦開發,真的假的?!

***

初體驗

兩年多前Teddy幫忙帶幾個實驗室的研究生開發看板軟體—ezKanban,採用類似Scrum的方式開發,兩個禮拜Teddy和學生review一次進度,每次review會跟學生討論設計細節,包含design review與code review。雖然Teddy覺得自己已經把話講得很清楚,但常常在下次review的時候才發現學生的實作並沒有真的達到Teddy的要求,所以需要再解釋一次。長期以往雖然還是有迭代與增量,但整個ezKanban的開發進度總是不如Teddy的預期。

約略一年多前有一次機會,Teddy在實驗室跟團隊一起mobbing,那次的體驗並不好。首先,實驗室的空間並不大,Teddy這個人懶散慣了,不太習慣幾人擠在一個沒有方便伸展與走動的空間。其次,Teddy年紀大了,雖然實驗室mob環境有一台65吋大螢幕,但Teddy看著這個螢幕還是有點不太習慣。寫到這裡Teddy才突然想到,不一定是螢幕的問題,也有可能是實驗室椅子太難坐的關係。畢竟平常在家裡工作,Teddy坐的都是Herman Miller Aeron的高級人體工學椅XD。

總之Teddy的第一次mobbing經驗並沒有激起到自己繼續跟學生一起mobbing的想法。


▲要價不菲的人體工學椅

***

疫情的正面影響

為了Clean Architecture與領域驅動設計(Domain-Driven Design;DDD)課程的教學需要,Teddy自己也開發一套看板系統—cleanKanban,但因為Teddy前端不熟,所以cleanKanban一直就只有後端。

去年七月初暑假的時候,Teddy覺得這樣下去也不是辦法,ezKanban團隊開發一套看板系統,Teddy自己也開發一套。ezKanban團隊的前端做得還可以,但後端還差很多。Teddy的cleanKanban剛好相反,後端好棒棒,但前端……醒醒吧,你根本沒前端啊!

因為新冠肺炎疫情的關係,人們開始廣泛接受以線上活動取代實體活動。Teddy也受到這個正面的影響,去年七月暑假剛開始,Teddy決定把cleanKanban合併到ezKanban裡面,並且跟ezKanban團隊一起mobbing。但Teddy採用skype遠端連線的方式,ezKanban團隊在實驗室mobbing,並分享桌面給Teddy。如此一來,Teddy不用出門,可以舒舒服服坐在家裡的電腦前面玩貓兼用嘴吧寫程式

有此工作環境,夫復何求!

▲Teddy在家裡遠端參與ezKanban團隊的mobbing實況


***

全新的體驗

換個合作模式之後,去年暑假ezKanban進度大爆發。Teddy覺很棒,於是暑假過後繼續跟學生約定每周mobbing的時間。

▲上學期ezKanban團隊mobbing時間表


從去年至今Teddy已經連續和ezKanban團隊mobbing了近七個月,每周平均約20小時,累積mobbing時數應該已超過500小時。

下一集Teddy從敏捷與精實開發的角度來細談為什麼mobbing是一種好的開發模式。

***

友藏內心獨白:看似浪費實則精實。

2021年1月19日 星期二

領域驅動設計學習筆記(16):領域模型要放什麼東西?

Jan. 19 10:00~11:07


客人的問題

上周末在【領域驅動設計與簡潔架構入門實作班】有學員問Teddy…

學員:DDD的building blocks包含Entity、Value Object、Aggregate、Service、Repository、Factory等,為什麼你的domain model裡面沒有畫出Repository?

Teddy:為什麼要在domain model畫Repository?

學員:因為我們要「派工」啊。

Teddy:派工 ?!

學員:對啊,我們要畫設計圖,然後派工給工程師去寫程式。

***

領域模型作為瞭解問題的工具

領域驅動設計是透過「建立領域模型」來驅動整個軟體開發設計的方法。模型屬於solution domain,如果採用最常見的物件導向分析設計方法,領域模型的主角就是代表問題領域裡面重要概念的「物件」。

例如,開發看板系統,在「看板系統」這個問題領域中,重要的概念包含Kanban Board、Workflow、Stage、Swimlane、Kanban Card、WIP Limit、Flow、Lead Time等,它們自然成為領域模型的當然候選人。

你在跟看板的領域專家討論問題時,會出現Repository或Factory這種概念嗎?應該不會吧!所以,你不會在領域模型看到它們。

***

但是我要派工啊

好、好、好,Teddy知道你要派工。領域模型首先作為了解問題領域的分析工具,最後當然還是要用程式碼實作出來。傳統OOAD透過「模型轉換」過程,將分析模型轉成設計模型再轉成實作模型。但DDD希望針對同一個bounded context,整個開發過程就只有一個模型,用這個模型作為領域專家與團隊的通用語言(ubiquitous language),最後再將通用語言用程式碼實作出來,達到ubiquitous language in code的境界。

想要將DDD的領域模型再轉成詳盡的設計模型然後拿給程式設計師去寫程式(完成一個派工的動作XD),是waterfall的做法。如果號稱使用DDD但還是採用這種做法,代表團隊根本沒有共通語言,所以才需要使用另一種語言(派工文件)來溝通

看到領域模型中的Aggregate,自然就知道需要搭配一個Repository來負擔持久化(把Aggregate狀態存到資料庫中)的責任,根本不需要在領域模型裡面表達這種關係。

***

DDD為什麼流行?

DDD藍皮書2004年出版至今也過了10幾年,為什麼近幾年才流行?Teddy認為除了很多人想透過DDD來開發微服務系統以外,另一個原因就是DDD、Event Storming與Clean Architecture、TDD這些做法全部加起來,非常適合用來作為落實敏捷開發的一種實踐方法,符合目前整個軟體開發的主流做法,做得好可以讓你的軟體變軟。

軟體變軟,才能夠支撐公司業務變得更敏捷(靈活)。DDD是domain-driven design,不要搞成document-driven design。

***

友藏內心獨白:軟體開發的特性就是「按圖施工,保證不成功。」

2020年12月27日 星期日

領域驅動設計學習筆記(12):Event Storming與User Story

Dec. 27 20:05~21:45

▲ezKanban團隊的開發流程


問題

有朋友問Teddy:「Event Storming做完之後,如何轉成user story與團隊溝通?」

在ezKanban開發過程中,團隊的開發流程從2020年暑假開始從Scrum轉成Kanban。團隊不再額外寫In order too [獲得什麼好處]….As a user, I want to [做什麼事] 這種格式的user story,而是直接將Event Storming的Command變成Use Case放到product backlog裡面。

傳統OOAD的Use Case其粒度一般來講比user story要大,一個Use Case可能有多個執行路徑或多個劇情,每一個執行路徑可視為一個user story。所以直接以Use Case來取代user story,有可能會違反敏捷與精實開發的小批量生產原則—開發的功能儘量切小,可獲得較短的交期(lead time)。不但bug會比較少,品質較好,也可以快速收集使用者回饋。

ezKanban團隊在Event Storming的過過程中不是從CRUD的角度來尋找領域事件,而是找出任務導向(task-oriented)的領域事件,並且採用TDD/BDD/SBE的方式實作Use Case。因此從實作面的角度,也是以step by step、piece by piece、scenario by scenario的方式完成使用者需求,並不會因為沒有將Use Case改寫成user story就造成溝通或是開發上的問題。

***

範例說明:Move Lane使用案例

圖1:Move Lane Command


視覺化是實施看板方法的第一條原則,因此ezKanban軟體自然要提供設計工作流程的功能,讓使用者可以新增Stage(垂直的工作階段)與SwimLane(水平的工作階段)。除此之外,使用者在視覺化工作流程的時候經常需要調整工作流程的順序與層級,因此Move Lane(移動工作階段)使用案例也很重要,如圖1所示。

使用者可以直接用滑鼠將最上層的工作階段拖拉到想要的順序,如圖2所示,使用者正在把Reviewed移到Ready to Deploy之後。


圖2:實作完成的Move Lane畫面

***

實作Move Lane使用案例

因為採用TDD方式開發,因此先撰寫第一個Use Case的測試案例—移動最上層的Stage。請參考圖3,這個測試案例代表最常見使用情況的happy path。


圖3:Move Lane的第一個測試案例

一開始這個測試案例根本無法編譯,因為都還沒寫production code。接著就按照TDD的流程,以最簡單的方式撰寫讓測試案例可以通過的production code,再透過重構來改善設計。

因為移動工作階段這個功能比較複雜,光靠Use Case的驗收測試無法涵蓋所有可能的移動情況,因此以specification by example的精神,以單元測試來代表移動工作階段的各種可能狀況,單元測試案例執行結果請參考圖4。


圖4:Workflow身上的moveLane方法的單元測試


針對move lane的各種狀況,ezKanban團隊目前一共寫了9個單元測試,從單元測試的名稱就可以很清楚看到所想要涵蓋的情況,例如以下兩個單元測試代表移動工作階段的邊界條件測案例。

  • should have correct order when move substage0 from order0 to order0 in the same parent
  • should have correct order when move substage4 from order4 to order4 in the same parent

單元測通過之後,回頭跑Use Case測試案例,順利通過就完成Move Lane使用案例的第一個happy path。

接著撰寫第二個Move Lane使用案例的驗收測試—should succeed when move second root stage containing sublane to first root stage,這個案例比較複雜,代表兩個平行的stage A 和stage B,其中stage B底下還有其他的stages,然後把stage B移到stage A底下。

這兩個驗收測試都通過之後,就可以撰寫rest controller,完成後再寫前端的react程式,把前後端接起來這個Move Lane功能就完成了……第一版。

上述提到兩個驗收測試都是移動Stage,還需要增加移動SwimLane的驗收測試,測試通過後整個Move Lane使用案例才可以算是完成。

目前ezKanban團隊的TDD僅限於use cases與entities這兩層的物件,這兩層以外的開發並沒有採用TDD,而是採用傳統code first方式。

***

通用語言表現在程式碼

領域驅動開發有一個重要的觀念—Ubiquitous Language in Code,能夠做到這個層次,開發團隊本身,以及開發團隊與stakeholders(尤其是domain experts)的溝通也就沒什麼問題。

Teddy整合了DDD、Event Storming、Clean Architecture與TDD/BDD/SBE,首先透過event storming建立ubiquitous language 與domain model,接著透過clean architecture與TDD將ubiquitous language落實在程式碼之中。以上這些都做到之後,如果覺得還是需要撰寫傳統的user story,那也沒關係,就去寫吧。

***

友藏內心獨白:到了離,就不用守了。

2020年10月30日 星期五

能力 = 紀律 * 技能

Oct. 31 17:29~18:10


在北科資工所兼課這幾年,Teddy在每學期第一堂課都會與學生約法三章,要求學生上課:

  • 不能遲到
  • 不可以使用手機、筆電等3C產品
  • 要參與課堂互動

如果同意再修這門課。

***

曾經有一次在檢討考卷的時候學生拿手機拍照被Teddy發現,學生的理由是:「我看不到投影幕上面的字,所以拍照起來放大」。Teddy只能佩服這位學生的臨場反應能力真的有夠敏捷,但什麼理由都不是理由,違反規定一律請出教室。

還有一次在分組討論的時候有學生使用手機,他的理由很正當:「我在找討論內容的資料」。沒什麼好說的,還是請他離開教室。

最近一次是學生把手機放在桌上,用手指滑了幾下手機。這位學生的理由是:「我沒有玩手機,是手機螢幕上有灰塵,我把灰塵清走」。好吧,這位愛乾淨的同學,下次上課再見。

***

Teddy是台北工專電子科畢業的,在那個時代,念書除了學習技能(skill),也很講求紀律(discipline)。能力再好,做事態度隨隨便便,無法成為一位專業的工程師。

幾年前讀了《Management 3.0: Leading Agile Developers, Developing Agile Leaders》,書中提到:

discipline * skill = competence(紀律 * 技能 =  能力)

Teddy看了是相當認同。紀律,不是要無腦的聽老闆命令,或是拍老闆馬屁,而是要遵守團隊或專業領域中做事的共同規範。例如,你自認自己能力很強,沒把團隊的DoD(Definition of Done)當一回事;開會姍姍來遲;不遵守團隊的working agreement。這種人,除非真的是千年一遇的軟體奇才需要特別加以保護,否則不用也罷。

***

在學校上課,學習技能是基本的,但學習紀律的機會就比較少。Teddy兼任的課程剛好都是敏捷開發相關課程,敏捷開發講的就是軟體,在課程中加入一些「紀律」的要求,也是理所當然之事,絕對不是Teddy心理變態想要刁難學生。

***

友藏內心獨白:有時候不須講理由,只需承擔結果。

2020年8月21日 星期五

三種團隊互動模式

August 21 18:15~19:01


前言

Teddy跟著ezKanban團隊兩年的時間,帶著幾位北科資工研究生開發ezKanban系統。當初是以DDD與Clean Architecture為研究主題當作切入點,這兩年來雖然有點進度,但軟體本身卻還沒達到Teddy認為可釋出的標準。

這個兩年的lead time真的有點長,今年七月初放暑假時,Teddy決定利用暑假時間和ezKanban團隊採用remote mobbing的方式一起開發。學生在學校實驗室,Teddy在家裡,透過Skype分享桌面的方式開發ezKanban,一個多月下來頗有進展。

***

幾個禮拜前在Amazon亂買書,買了一本《Team Topologies: Organizing Business and Technology Teams for Fast Flow》,有一天晚上睡不著翻了一下,沒想到還挺有趣的。書中提到四種團隊型態以及三種互動模式,特別適合軟體開發公司。今天用Teddy這兩年來和ezKanban團隊的合作模式,解釋書中提到的三種互動模式。

***

X-as-a-Service

將另一個團隊視為一種服務來使用,團隊之間只有最少的協作。這種情況發生在學生遇到問題詢問Teddy的時候,此時Teddy-as-a-Service,提供學生某種解答,這個過程中雙方並沒有很多協作關係。

***

Facilitating

協助另一個團隊釐清阻礙,這種情況發生在兩周一次的sprint review。Teddy會看學生的設計與程式碼,觀察有沒有什麼設計問題是學生沒有看出來的,並與他們討論並訂定後續修正計畫。下個sprint review會繼續相同步驟,經過迭代的過層,系統設計品質越來越好。

但是,因為兩周review一次兩小時,說真的能夠看到的程式碼真的不多,因此每個迭代的增量幅度相對來講就比較有限。另外一個問題是,因為學生還在學習的過程,很多Teddy沒有看到的地方,雖然程式可以動,但設計通常都存在一些大大小小的問題,長期而言阻礙系統的可維護性。

***

Collaboration

兩個團隊彼此緊密合作。在今年暑假之前,Teddy和ezKanban團隊的關係幾乎不存在Collaboration(協作)。之前也幾度思考過是不是跟學生一起寫程式,但又擔心如此一來學生偷懶,過於依賴Teddy,搞到後來變成Teddy幫他們做碩士論文研究 Orz,因此就作罷。

但隨著ezKanban慢慢成形,但又無法達到Teddy心中的釋出要求,有種恨鐵不成鋼的遺憾。幾經思考,加上今年因為因為疫情的緣故,泰迪軟體生意比較清淡,Teddy空閒時間也比較多,因此決定「撩落去」,利用暑假時間一起合作開發。

***

成本不同

這三種互動模式,適合解決的問題不同,成本也不同。對Teddy而言,開發軟體如果可以和團隊緊密合作當然是效果最好的。畢竟Teddy也比學生多活了20幾年,還是比他們更能夠看出設計不合適之處。

除了學生獲益,Teddy也從這個過程中獲得很多design bad smells的寶貴範例,平常可能想破頭都不一定生的出來這些例子。這是一個雙方都互相學習的過程。

很多時候,想要成事,還是需要走到前線,親自動手。

***

友藏內心獨白:If you stop coding, you stop learning, by Kent Beck.

2020年7月17日 星期五

領域驅動設計學習筆記(9):Query和Read Model

July 17 12:40~13:48

▲圖1:解釋所有事情的一張圖,取自《Introducing EventStorming


背景介紹

在Alberto Brandolini的《Introducing EventStorming》書中,有一張圖用來解釋他對於採用event storming塑模問題的想法,如圖1。


▲圖2:解釋所有事情的一張圖,取自《Introducing EventStorming


把圖1用event storming展開,出現如圖2所示的流程:

  1. 使用者執行Aggregate身上的Command。例如,在看板系統中,使用者新增看板(Create Board)、移動卡片(Move Card)。
  2. Aggregate執行完Command後發出Domain Event,用來代表系統狀態發生改變。例如看板已新增(BoardCreated)、卡片已移動(CardMoved),
  3. Domain Event也可能由External System所產生,例如,收到銀行傳來的付款確認(PaymentConfirmed)事件。
  4. 剛剛上述行為屬於Write Model,也就是執行Command改變系統狀態並產生領域事件。但使用者不只會改變系統狀態,也需要讀取系統資訊,以決定要執行哪一個Command。表達讀取資訊的模型稱為Read Model,它可以從改變狀態的領域事件而轉換出Read Model。
  5. 將這個Read Model傳給UI,可以做出最後使用者看到的系統畫面。
  6. 使用者看到系統畫面,或更抽象的表示,看到Read Model,從而做出決定,執行下一個Command。
  7. 圖2中的Policy表示發生領域事件之後需要後續處理的事情。例如,當使用者註冊成功,則寄發啟動帳戶的email。

***

Event Storming + Clean Architecture

▲圖3:cleanKanban範例,將Read Model簡化成Use Case的Input


Teddy在上〈領域驅動設計與簡潔架構入門實作班〉教導學員用Event Storming來建立Domain Model與Ubiquitous Language。在實作面,套用Clean Architecture,並採用TDD/SBE的方式來撰寫程式。因為Clean Architecture最核心之處在於Entity Layer與Use Case Layer,把UI和DB都當成技術細節,因此在塑模時先不考慮。

所以Teddy把原本Event Storming的綠色便利貼,由原本的Read Model,簡化為使用者看完Read Model之後,決定要執行哪一個Command所給予的參數。例如,顧客去麥當勞櫃台點餐,他看到Menu上面有1號餐、2號餐、N號餐等,這個Menu就是Read Model。最後使用者決定要點一份5號餐,外加一份大薯條,這些資料就是Input。

***

加上Query

如果只是實作後端,從Clean Architecture的角度來看,上面這些分別代表Command、Aggregate、Domain Event、Policy、Read Model(Input)以及UI 的便利貼,也就夠了。但實際開發完整的軟體需要接上前端,此時發現少了一種便利貼:Query

Command是會改變系統狀態不傳回值的操作,Query則相反,不會改變系統狀態但會回傳值。圖1與圖2中,Domain Event—>Read Model之間,應該還需要補上Query,這樣子參考Event Storming轉成程式碼的時候就可以有一對一的對應關係,如圖4所示。


▲圖4:ezKanban範例,由Query (Get Home Content) 產生Read Model (或稱為View Model),再將此Read Model顯示在UI上。


在圖4中,Query並不像Command需要傳給Aggregate,其實作方式可以透過撰寫Read Repository以及搭配Projection (將原本適合用來寫入的Write Model資料映射成適合讀取的Read Model資料)。

***

結論

加上Query之後,整個Event Storming的便利貼就更完整,也可以和UX/UI設計師討論人機互動的問題。

但是,加上Query與UI,整個模型也變得更加複雜。從軟體架構的角度來看,是否在軟體開發初期就需要增加這樣的複雜度,也是一個可以討論的議題。當然可以只針對Core Domain以及支援商業流程的主要Read Model優先設計畫面,其他次要部份採用迭代與增量的方式來實作,也是一種選擇。

***

友藏內心獨白:Model 不是越詳細越好,只要能表達problem domain我們所關心的問題就好。

2020年6月20日 星期六

為什麼不反駁?

June 01 09:20~10:26

▲學生發問的問題,圖由學生所繪製


好問題

Teddy這學期在北科教軟體架構課程,設計一個專案給學生練習,題目是:「用DDD + Clean Architecture + TDD,開發看板系統的後端。」這禮拜四在北科上課,學生DD問:「Calculate cycle time這個Command要傳送給哪一個Aggregate?還是應該把這個Command實作成一個Application service?」

由於這整學期Teddy都在教學生如何把Event Storming的產出透過一致性、固定的步驟,在套用Clean Architecture的前提之下,採用TDD方式將設計轉成程式碼,以達到Ubiquitous Language in Code的目的。因此當學生認為他們找不到合適的Aggregate來接收Command,Teddy直覺反應就是:「因為這個Aggregate還沒有存在你們的domain model裡面,是你們還沒開始找,而不是沒有合適的Aggregate。

後來Teddy詢問每一組,得到的回答大都是「沒有合適的Aggregate,直接把這個Command做成Application service。」只有一組將這個Command送給Workflow Aggregate,但Teddy覺得送給Workflow並不合適,因為Workflow沒有Card移動的資料,無法計算cycle time。

***

公堂之上大膽假設

最後Teddy提議增加Cycle Time Calculator這個Class,由它負責計算cycle time,並寫了一段程式碼給學生參考。

Teddy:Cycle Time Calculator是一個沒有狀態的service類別,service有兩種:domain service與application service,它應該屬於domain service,放在clean architecture的entity層。

學生DD:可是老師你說接收Command的對象是Aggregate,而Aggregate是有ID與狀態的類別。但是Cycle Time Calculator是service,它並沒有狀態,這樣不是違反了之前design level event storming的規範?

討論到這裡Teddy不得不稱讚一下這位同學,因為這個問題Teddy之前也沒想過…

Teddy:將design level event storming轉成clean architecture的程式碼的方法是我們自己定義的,我們可以加上一個規則「Aggregate與Service都可以接受Command」。

這種說法感覺有點硬坳,但乍看之下也還說得過去。

***

忙到忘記

回家之後過了一會Teddy才想起來:「這個問題之前和ezKanban團隊開會時曾經討論過!」當時Teddy告訴團隊:「計算cycle time是在Reporting bounded context,它不是看板系統的core domain。因為它是產生報表的domain,是唯讀的,所以我們不一定要套用DDD或是OOAD的方式來建這個reporting domain,也許可以用transaction script就可以產生報表。

但後來Teddy沒有進一步與ezKanban團隊review他們實作reporting domain的程式碼,所以細節上他們到底如何實作Teddy也不知道。

***

討論無身分高低

在這禮拜四上課時Teddy曾詢問ezKanban的人如何實作Calculate cycle time?只得到「設計成service」的答案。後來Teddy發表一堆「公堂之上大膽假設」的言論,ezKanban的人也沒跳出來 打槍 提醒Teddy,這個問題之前曾經討論過。

可能學生預設認為上課只是來聽老師開講,把所謂的「正確答案」記下來就好,不要質疑老師,也儘量不要跟老師對上眼、說上話。

但Teddy的課程不一樣,第一個禮拜上課Teddy就告訴學生:「叫我Teddy就好,不用叫我老師。別的課我不知道,但在我的課堂上我們的關係是平等的,不是上下階級。我只是在課題上扮演老師的角色,你們扮演學生的角色,僅此而已。修這門課我會問很多問題,你要願意與我互動,再來修這門課,不然就請退選。

▲課堂上老師與學生的關係是A還是B?


雖然第一周上課Teddy就把話說得很清楚,但學生心裡還是千百個不願意,可能還是怕被老師擺一道,萬一多說話得罪「方丈」日子就不好過了。但專業討論不應有身分高低,如果學生心中有看法,但礙於身分不敢說,這其實是陷老師於不義,阻礙了課堂上探索知識的機會

***

反思

敏捷開發強調要透明,唯有透明,探索調適才可能發生作用。很顯然Teddy與學生之間的互信可能還不夠,導致學生有看法不好意思公開表達。

不過,學生當下沒提出來也不能排除另一個原因,就是學生自己也忘了,根本沒想那麼多。

***

友藏內心獨白:腦補得太厲害。

2020年5月13日 星期三

學習總在下課後

May 13 18:11~18:59

▲別人的水總是比較甜,江山易改,喵性難移。


行為是否改變

成立泰迪軟體這幾年,遇到企業內訓課程,有些HR問Teddy:「能不能幫忙在課堂上看看誰比較認真,或是出考卷作為訓練成果評量,這樣學生才比較會專心上課。」

這些事當然都可以配合,但我總是跟對方說:「我覺得最好的評量就是同仁上完課之後行為有沒有改變,考試、打上課成績,頂多就是因應公司規定滿足交差的形式要求。」

上完課之後行為有沒有改變,是一件需要後續追蹤的工作,也是一件耗費時間與金錢的工作,絕大多數的公司都覺得:「公司已經出錢辦教育訓練,上完課之後同仁應該就具備那種能力才對。」

除非課程內容難易度不高,否則要求學員上完課之後立刻變成高手是不太可能的事情。不可諱言,有些天賦異稟或是努力很久終於時機成熟的同仁可以在上課當下「開悟」,課程結束之後行為為隨之改變,但大部分的同仁可以藉由上課「獲取新知」就已經很不容易了,要求他們上完課後立刻可以排除萬難做出有意義且持久的改變,實屬不易。就好像看了場精采的電影,觀眾(同仁)在心中對劇情與演員的表現留下深刻印象。但要讓觀眾看完電影之後就變身為導演,可以開工拍戲,那還需要長時間的努力。

下課之後回到工作上,面對的又是現實問題,被一大堆代辦事項追著跑,如果沒有外力協助,光靠員工自己很難有辦法靜下來思考如何改善。與其花錢辦大量的訓練課程,還不如思考是否有可能把部分上課費用拿來找顧問、教練,在課程結束之後和團隊一起,協助團隊改變行為,並且將改變後的行為固定下來,變成新的工作方式

***

終身學習

奧修在《草木自己生長》提到:「知識是借來的,而學習是你自己的;知識是透過文字、語言、和觀念,而學習是透過經驗;知識總是一個結束,你知道了它,它就結束了;學習永遠沒有結束,它總是在途中;學習是一個過程…」

上課可以獲得知識(收集名詞),但學習必須靠自己,它是一個終身的過程。很多教育訓練成效不彰,因為來上課的人頂多只想獲得(收集)知識,而不想學習。教的人也只是講述知識,沒有鼓勵學習。

Teddy相信:「學習在下課之後才真正開始。」所以教學的主要目的,應該是讓學習者具備下課後持續學習的能力。為了達到這個能力,需要介紹基本的領域知識以及設計適合的體驗活動,讓學習者在一個比較輕鬆的情境下獲得這些知識與體驗。

***

友藏內心獨白:學習是軟體開發的瓶頸。

2020年5月12日 星期二

等一個人春天

May 12 14:25~15:50

▲浪浪也會自己生長。


緣起

Domain-Driven Design: Tacking Complexity in the Heart of Software》這本書出版於2004年,當時Teddy正在念博士班,忙著準備資格考與論文,根本不知道有這本書。後來好像曾經聽過DDD這個名詞,當時自己一直把它腦補成MDA(Model-Driven Architecture)。因為自己對MDA沒什麼好感,當然對DDD也就沒興趣 Orz。

***

一面之緣

2013年,在這本書出版9年之後,Teddy忘了在什麼機緣之下買了它。翻了翻書的內容,覺得:

  • Model-Driven Design和以前學過的OOAD(物件導向分析與設計)建domain model的內容大同小異。
  • Ubiquitous Language是一個新名詞,但是它目的和Alexander的Pattern Language很像—A common ground for communication,而且比Pattern Language還要簡單。

以上這兩個模式(Patterns)算是DDD的核心,先被Teddy給「鄙視」一番。繼續往下看,覺得比較「有用」的模式是:

  • Aggregate,將一群緊密相依的物件放在一起,這是一種人為的模組化單位,可以彌補一般程式語言在Package與Class之間的空白。
  • Context Map,表達bounded context之間的關係。

然後呢?就沒有然後了,書就被Teddy丟在一邊。

***

再續前緣

三年前指導教授想把他手邊的軟體架構這門課交給Teddy來教,Teddy參考了近10本軟體架構的書,最後選了當時剛出版的《Clean Architecture: A Craftsman's Guide to Software Structure and Design》。教軟體架構和DDD原本沒什麼直接的關係,因為實作Clean Architecture核心層(Entity Layer)需要建立domain model,而Teddy又不想只是使用原本OOAD的方法,想起DDD關於建立domain model的一些設計模式,於是又回頭探索DDD。

又過了一年,有一天Teddy在YouTube聽一個DDD演講,內容和講者是誰已經忘了,但是聽到一句ubiquitous language in code,好像被雷打到一樣,突然醒過來。這種感覺,就好像當年六祖慧能聽到金剛經的「應無所住而生其心」一樣,當下頓悟XD。

Alexander的pattern language使用對象是,人用它來設計住宅以及都市規劃。如果將pattern language應用在軟體開發上,則可以想像成套用了一群(數個)設計模式來解決一個大的設計問題,這也是原本Teddy對於ubiquitous language的看法。

但是,ubiquitous language不只是這樣。DDD的Model-Driven Design與Ubiquitous Language這兩個模式是一體兩面,如果只把它們應用在「概念層次」,只用來作為領域專家與開發團隊溝通的工具,那就只發揮了DDD一半的功力。還有另一半,而且是打通任督二脈的另一半,就是要將Ubiquitous Language表達在程式碼裡面。所以光是只談ubiquitous language還不夠,加上in code,才是畫龍點睛,才是幫佛像開光。

***

自然生長

Ubiquitous language in code,同樣一句話,當時的Teddy聽了有感覺,換成別人就不一定。就算是Teddy本人,如果早個幾年聽到這句話,也不一定有感覺。

奧修在《草木自己生長:禪的真隨》引用禪師齊內林的話:「靜靜地坐著,什麼事都不做,當春天來臨,草木就自己生長。」雖說「什麼事都不做」,實際上並不是「什麼事都不做」。而是以平常心,認真去做好每一件該做的事情

等到時機成熟,春天到來,草木自然生長。

***

友藏內心獨白:不能揠苗助長啊。

2020年5月11日 星期一

主要的事

May 11 21:34~22:49

▲愛是主要的,品種是次要的。


先因,後果

活到這把年紀,很多事情都看得比較開,執念也越來越少。但因為尚未悟道,還是有一些看不開的關卡,其中就是一個。

俗話說:「錢不是萬能,但沒錢萬萬不能」,沒人會嫌錢太多。Teddy自認對於賺錢的慾望相對而言已經保持在比較低的狀態,三不五時還會因為理念的關係推掉送上門的案子。但當生意比較不好的時候,雖然減少的收入完全不會造成生活困難,但心中還是不免會嘀咕一下,經濟成長率怎麼變負數勒。

這一陣子讀了奧修的《禪宗十牛圖》,書中提到(pp.168~170):

  • 非主要的事,你想要先確定它們,而你以為主要的事將會跟著來。要改變那種態度:先想主要的事,非主要的事就會跟著來。先想主要的事!什麼是主要的事?果並不是主要的,因才是主要的;別人並不是主要的,你才是主要的。
  • 比方說,人們認為如果他們能夠賺足夠的錢,他們就能夠快樂,但事實上事情並非如此。如果你很快樂,你就會很富有……一個快樂的人不可能不是如此。他或許沒有大的皇宮,但他仍是富有的。他或許是一個街上的乞丐,但他仍是富有的。但是你先試著去擁有很多財富,然後你認為你就會快樂,它從來不以那樣的方式發生,因為財富不可能是快樂的一個原因。快樂一直都是財富的一個原因
  • 試著深入去看你的本質,並且想想主要的事情。要很快樂!就在這個片刻你就可以快樂,沒有人在擋你的路。如果你無法在這個片刻就快樂,你將永遠無法快樂。快樂跟未來無關。快樂不知道有明天,因為快樂並不依靠其他任何東西,它只是一種態度,就以你這樣,你現在就可以快樂。
  • 你可以根本沒有理由地快樂,因為快樂是很多事情的理由,它是基本的因。你可以快樂,試試看。你一直都從另外一端來嘗試,現在從基本的因來嘗試。首先有那個因—成為快樂的—然後果將會隨之而來。永遠都要記住不要去找代罪羔羊,那樣做一定會錯過你的生命。

為什麼要賺錢?如果是為了快樂,錢的確可以帶來快樂,但也有很多快樂是錢無法買得到的。錢應該是果,而不是因。是某種主要目標的副產品,但它不是,也不應該是主要的事。

***

價值驅動的極致

關於「錢不是一切」的相關說法已經聽過很多,但奧修的闡述方式,讓Teddy豁然開朗。錢只是一個例子,書中的重點是:弄清楚什麼才是主要的事什麼是因

泰迪軟體成立的是什麼?公司當然要獲利、要生存、要賺錢,但賺錢絕對不是它的因。如果賺錢是主要的因,Teddy應該要專研拍馬屁、欺上瞞下,這樣賺錢還更輕鬆。

回到Teddy的初衷,希望能找到一個讓自己可持續追求The Timeless Way of Software Development的生存模式:不用看老闆、主管、同事的臉色,沒有辦公室政治,只要專心對付(服務)客戶即可。只要這個「因」做對了,錢應該也會隨之而來。

這個道理可以應用在各種場合。以軟體開發而言,什麼是主要的事?人與互動是主要的事,還是流程與工具(敏捷宣言第一條)?軟體架構與設計模式是主要的事,還是程式語言與框架?願景與里程碑(目標)是主要的事,還是User Story以及Tasks?通用語言與領域模型是主要的事,還是Aggregate、Entity、Value Object與Repository?

弄清楚主要的事,才不會瞎忙,心才會靜下來做出正確的決定。

***

靜心

今年因為疫情的關係,泰迪軟體的很多課程都因為招生不足而停開。如果是往年Teddy多多少少都還是有點焦慮。但此時此刻,雖然收入比去年少很多,但內心卻異常平靜。

因為Teddy知道,自己持續在做主要的事。而且,想要快樂就可以快樂,不需要等賺大錢。

***

友藏內心獨白:這是一種阿Q精神勝利法嗎XD。

2020年5月4日 星期一

變成一個點

May 04 09:52~11:19

▲學習的過程就是填滿圖形。


加法

老子說:「為學日益」。學習者,心中有一個學習的標的物(目標),例如設計模式、敏捷開發、軟體架構、人工智慧、大數據、領域驅動開發、測試驅動開發等。

這個目標,在學習者的心中形成某種形狀form),它可能長得像圓形、三角形、不規則圖形。這個形狀的邊界(boundary)界定了學習者的守備範圍,邊界以內屬於學習內容,邊界以外屬於脈絡、背景知識(context)。隨著學習進程,範圍可能會改變,但它總是一個有邊界的形狀,不管這個形狀長得像什麼圖形。

這個形狀一開始是空的,因為學習者還沒學會所需的知識。他藉由讀書、上課、討論、Google、練習、參加社群聚會、模仿、實作等方式,充實自己,填滿這個圖形。

***

智光忽明忽滅

▲從某個角度來看,形狀好像已經填滿了。

在某些片刻,學習者一度以為自己已經完全掌握學習內容,他可以正確回答許多(特定)問題,相當於考試獲得100分的喜悅。他自得意滿地認為,形狀好像已經填滿了。

***

▲換個角度才發現空白處還很多。

但換個角度、換個環境、換個題目、換個發問方式、換個時間、換個專案、換個公司,此時才發現原來形狀之中空白之處還很多,還需要精進學習。

***

▲總是還有填不滿的空隙。

有一天,這位學習者越來越博學,領域知識的名詞幾乎都已經收集得差不多了,如果參加全國性考試一定可以得高分。但是,形狀還沒填滿,還是有一些空隙。

***

變成厲害的人

敏捷宣言說:

藉著親自協助他人進行軟體開發,
我們正致力於發掘更優良的軟體開發方法。

(We are uncovering better ways of developing
software by doing it and helping others do it.)

學習者自己變得厲害只是過程中的一個階段,他還需要練習幫助別人,讓別人在他的幫助之下,有能力完成原本認為很困難無法做到的事情,成為Kent Beck所說的那種「厲害的人」。

▲到了這個時候,形狀已經填滿。

***

減法

▲Quality without a name,特質本身非常具體,變成一個點。它沒有面積,不再是形狀。

老子的道德經在「為學日益」之後還有幾句:「為道日損,損之又損,以至於無為,無為而無不為。」知識的累積,越來越多,就算此時自己覺得已經「收穫滿滿」,但這個收穫滿滿轉眼間又變成空。因為環境改變、領域改變、競爭者改變、專案改變、團隊改變、慾望改變,你滿別人比你更滿,不滿之心因而升起。

心永遠靜不下來。

Alexander在《Timeless Way of Building》書中提到學習模式(pattern)的過程也是如此,透過學習模式來重拾人們設計與建造建築的能力。但這不是終點,不是目的。透過這個過程,喚回人原有之本性、本能。之後,這些模式都要忘記。

金剛經云:「知我說法,如筏喻者,法尚應捨,何況非法。」聽佛說法(學習pattern),是為了開悟(追求quality)。有了quality之後,那些names、那些patterns,就可以捨棄了。

***

友藏內心獨白:試求未開悟前之心理陰影面積。

2020年4月27日 星期一

怎麼當好一個ScrumMaster?

April 27 12:46~14:15


演給你看不好嗎?

接觸過Scrum鄉民應該都會同意,Scrum的三個角色:Product Owner、Development Team與ScrumMaster,其中ScrumMaste是最抽象、虛無飄渺的存在,最不容易了解,也最難扮演好這個角色。

曾經有幾位客戶要求Teddy到他們家去充當短期的ScrumMaster。這是一個很合理的速成方法,如果請有經驗的人來當ScrumMaster,團隊的新手ScrumMaster就有一個「具體的範本」可以參考。日後只要依樣畫葫蘆照著做,應該八九不離十差不到哪裡去。

但就算是在缺少業績的情況下,Teddy從來沒有答應過客戶去當短期的ScrumMaster。Teddy可以當敏捷顧問、敏捷教練、碎碎念講師(看你喜歡哪個稱呼),陪著PO、團隊與ScrumMaster一起成長,但無法擔任團隊的短期ScrumMaster。

為什麼?因為Teddy始終是個外人。ScrumMaster與Product Owner和Development Team之間是一種很緊密的團隊合作關係,唯有長期承諾,讓整個團隊知道「你是在玩真的」,這種關係才會是完整的。外在環境、專案特性、團隊成員等不斷地變化,沒有固定的公式可以遵循,很能靠一個外人「演給你看」就可以了解箇中精隨。

ScrumMaster必須現時現地去體驗各種作用力、各種限制、各種壓力、各種機會,並且不斷地嘗試、調整。只是短時間加入團隊的「外派」ScrumMaster頂多在敏捷形式提供團隊參考,但這種參考非常片面,也很容易讓人誤解。

***

動輒得咎

書上說,ScrumMaster要協助團隊排除阻礙,因此有些ScrumMaster從這一點著手,最後變成團隊有任何問題都找ScrumMaster來處理,團隊變成了媽寶,而ScrumMaster變成了寶媽

書上又說,ScrumMaster採取僕人式領導(Servant-Leadership),從這一點出發,有些ScrumMaster最後成為團隊的僕人,負責幫團隊訂會議室、安排議程、寄發會議通知、撰寫會議記錄、更新Scrum Board,以及催大家準時參加Scrum會議。僕人易做,領導難行。

也有人覺得引導技能很重要,ScrumMaster要引導團隊發現自身的問題,而不是跳下去幫團隊解決問題。再加上Scrum團隊是自組織團隊,應該有能力能夠從失敗中學習,逐漸改善。因此,當團隊成員向ScrumMaster求助時,ScrumMaster總是說:「由團隊決定」,弄到最後讓團隊成員覺得ScrumMaster的存在本身變成了一種阻礙。

***

云何應住?

依據Teddy的理解,ScrumMaster之所以存在,他要解決的問題是:「如何讓Development Team、Product Owner與組織獲得Scrum的好處?」可以從這個角度切入,來思考要怎麼做好ScrumMaster。

金剛經裡面有一段話:

「菩薩於法,應無所住,行於布施,所謂不住色布施,不住聲香味觸法布施。須菩提!菩薩應如是布施,不住於相。何以故?若菩薩不住相布施,其福德不可思量。」

把「菩薩」換成ScrumMaster,就可以知道ScrumMaster要怎麼做。

ScrumMaster依據敏捷精神,無須拘泥一定的形式與方法,只求能夠幫助開發團隊、PO與組織。不需拘泥教練、僕人領導、流程專家、干擾屏蔽、阻礙排除、變革代理。ScrumMaster要專注在協助開發團隊、PO與組織獲得敏捷的好處,不拘泥於特定形式,如此便可產生有利的效果。

有些ScrumMaster很認真,為了做好自己的工作,上了很多課。但回到團隊之後,如果做得好,他變成了一個教練、一個僕人式領導者、一個流程專家、一個保護者、一個石頭搬運者、一個敏捷轉型策略大師。做不好,他成為沒打過棒球的棒球教練、任勞任怨的僕人、技術控、太極高手(推拖工作)、吵架王、嘴砲王。

他已經不是ScrumMaster,他被切割成好幾塊

***

無為而無不為

扯了這麼多,到底ScrumMaster要怎麼做?很簡單,在敏捷精神之下,該做什麼,就做什麼。做了之後有幫助,就持續下去。沒有幫助,思考如何改善,不斷精進。

這個過程很漫長、很痛苦,因為現實世界很危險,你要靜心,忍得住孤獨、寂寞、羞辱。你可能需要一位師父、前輩、長者,與你同在,支持你走完這個過程。

最後,你渡過河,到達敏捷的彼岸,你有能力幫助其他ScrumMaster繼續他們的旅程。

***

友藏內心獨白:是在找聖人逆!

2020年4月20日 星期一

毛書一刀未剪大公開

April 20 11:28~12:45


Teddy在2012與2014年所出版的兩本書《笑談軟體工程:敏捷開發法的逆襲》與《笑談軟體工程:例外處理設計的逆襲》已經絕版一陣子了。上個周末上【Scrum敏捷方法實作班】,有學員知道書已絕版,詢問Teddy是否能提供第一本書的電子檔?Teddy也忘了之前有沒有公開提供過,所以今天就寫一篇文章把這兩本書的電子檔公開。

因為書本最後出版的格式經過悅知出版社編輯過,這個「排版編輯」版權屬於出版社,所以Teddy只能公開當初提供給出版社的原稿。編輯前的原稿與最後出版的內容略有差異,且可能有一些尚未訂正的錯字,請讀者自行除錯。

電子檔下載:

***

友藏內心獨白:紙本的比較香。

2020年3月11日 星期三

可是誤一生

March 11 12:36~13:47

▲Ada:我有問題!


可是思維

成立泰迪軟體這些年,教過的學員也有數幾千人次。Teddy很歡迎學員發問,從問題中可以了解學員的困難以及學習狀況,也可以檢驗自己的知識是否足以回答問題,並刺激自己思考的角度。

但有一種學員的反應讓Teddy很無言,Teddy稱之為可是學員,他們總是在聽了你的建議之後,經常使用可是作為拒絕改變的藉口。

***

可是學員:我們軟體的bug很多,有沒有什麼做法可以改善這個問題?

Teddy:你們有寫單元測試嗎?

可是學員:沒有。

Teddy:是不是可以考慮先從寫單元測試開始,至少可以確定基本軟體元件的正確性。

可是學員:可是我們工程師都沒時間啊,程式就寫不完了不可能要求他們寫單元測試。而且他們也不知道怎麼寫,專案時程那麼趕也沒時間讓他們學。要讓他們寫單元測試這不可能啦。

Teddy:Code review呢?

可是學員:可是我們工程師程度都不好,沒有能力review別人的程式。

Teddy:難道都沒有資深工程師嗎?

可是學員:是有幾位資深工程師,可是他們覺得code review很浪費時間,沒人想做。

Teddy:是不是可以跟團隊成員溝通,透過code review可以協助資淺人員能力成長,改善產品品質,提升整個團隊的能力?

可是學員:可是公司也不想浪費資深人員的時間,畢竟他們的薪水比較高。公司希望他們專心開發核心程式,而不是花時間去幫其他人做code review。

Teddy內心獨白: (我的棍子放在哪裡!)

Teddy:有試過Pair Programming嗎?

可是學員:我在社群活動中有聽過Pair Programming介紹,可是公司一定不會同意。專案那麼趕怎麼可能讓兩個人一起寫程式,這不可能,打死都不可能。

Teddy:那……找QA或工讀生,用人工測試呢?

可是學員:可是公司根本沒有QA部門,也不可能撥預算找工讀生來測試。

Teddy:那還有一招,外包給客戶,請客戶幫你們測了

可是學員:我們現在就是這樣啊。

***

挑戰現況

凡事把可是兩字掛在嘴邊,很可能是一種不思改變只想找藉口拒絕改變的反射性動作。如果只是朋友之間互相訴苦,並不是真的想解決問題,那倒也無妨。但若是工作上遇到問題也抱持著這種心態,就無法突破現況並有所改善。

軟體工程,特別是敏捷實務做法,並不是什麼艱深理論,都是經過多年、多人、多團隊、多公司的實務經驗。也許這些經驗與你自己目前狀況不符,但也不要立刻否定,先把它當成一種假設,然後思考要做出何種改變、要如何努力,才有可能讓假設成真。

學員:我們軟體的bug很多,有沒有什麼做法可以改善這個問題?

Teddy:你們有寫單元測試嗎?

學員:沒有。

Teddy:是不是可以考慮先從寫單元測試開始,至少可以確定基本軟體元件的正確性。

學員:我們工程師都忙到沒時間,程式寫不完了要如何讓他們願意寫單元測試?

Teddy:你們應該是把寫production code和test code 看作是兩件事,而工作上只有要求完成production code即可,才會有這種「程式都寫不完了哪有時間寫測試」的想法。

學員:對啊,不都是這樣嗎?

Teddy:我認為單元測試是開發不可分割的一部分,所以做完的定義不是只有production code寫好就可以,應該也要包含足夠的單元測試。這在敏捷開發(Scrum)裡面叫做DoD—Definition of Done。藉由調整DoD(逐步增強),可以改善團隊的產品品質。

學員:聽起來滿有趣的,但我們團隊目前都沒有這樣的觀念,還是停留在犧牲品質以便趕上進度的狀況。短期間或許可以交差,但是對公司來說中長期發展會受到傷害。像最近客戶對於品質不良的抱怨聲音就很大,而且團隊成員工作也沒有成就感,流動率很高。

Teddy:解決方法不一定只有或只是單元測試,還有很多種解決方案,依據你們專案的情境(Context)可以選擇不同的方式。

學員:我回去跟我老闆討論一下,看看有沒有可能找一個新的小專案來試看看,做出一些改變。

***

想解決問題但卻不想做出任何改變,只想從別人口中聽到「你好辛苦」、「你好委屈」、「你好棒棒」、「公司好爛」、「同事好混」這類的話,那還是不要開口問Teddy問題好了。

***

友藏內心獨白:這樣不就不溫柔了XD。

2020年3月10日 星期二

相依不相依

March 10 15:18~16:31

▲圖1:Customer/Supplier(客戶與供應商)關係圖


上下游相依性

相依性,或稱為依賴耦合,無論是在做人做事,或是在軟體開發上,都是一種讓人又愛又恨的特性。

父母對妳的男朋友不滿意,嫌他太窮、薪水太低,因此反對妳的婚事。妳不敢違背父母,也不想分手,因此一直處在進退兩難之間,終生大事的時程(schedule)因此被延誤了。身為專案經理的妳,卻是一點辦法都沒有。

你的部門需要客服部門提供API讓你們查詢並分析客訴情況與進貨廠商之間的關係,但是客服部門覺得這不是他們的工作,而且他們太忙根本沒有時間可以幫你們開發這個API。這件工作一直卡住,從老闆的眼中看來,工作交派給你的部門但卻一直沒有完成,你的部門因此黑掉,黑到發亮。

以上例子如圖1所示,女兒和父母之間的關係,你的部門和客服部門之間的關係,稱為Customer/Supplier(客戶與供應商),父母、客服部門是供應商,是價值鏈的上游(upstream),女兒、你的部門是客戶,是價值鏈的游(downstream)

***

解決方案

當兩個個體或組織屬於Customer/Supplier關係,而Supplier佔有絕對主導權或是完全不想鳥你的時候,身處於下游的Customer做起事來就會很辛苦。為了獲得父母的支援,妳可能選擇遵從他們的想法(Conformist):「好吧,既然父母不喜歡這個男朋友,我就再找一個合他們意的對象」,不然就擺爛裝傻雙方僵在哪邊。

除了完全服從之外,你還可以選擇切斷對上游的依賴。反正結婚後不想拿家裡的好處,就走自己的路(Separate Way)吧。

但如果你父母是好野人,完全切斷來自他們的幫助有時並不是明智之舉,因為妳可能會損失少奮鬥10年的機會。但妳又不想委屈自己放棄目前交往的對象,此時可以考慮找一個中間人,吸收父母之間對於妳男朋友不滿的負能量,在婚後一方面應付父母,另一方面保持自己家庭和樂。這個防止毀壞層Anticorruption Layer)可以由妳自己當任,或是找父母信任的第三人,例如家族中的開明長輩或自己的哥哥、姊姊。

以上三種解法:Conformist、Separate Way、Anticorruption Layer,就是在《Domain-Driven Design》書中提到的三種不同的Bounded Context之間的關係。

除了上述這三種關係,還有第四種選擇可以避免下游被上游綁架,就是套用Dependency Inverse Principle相依反轉原則),如圖2所示。


▲圖2:套用相依反轉原則改變上下游的相依性


你不用無止境地等待客服部門提供API,反之,幫你所需要使用的服務定義一個介面,然後便可以依據此介面開始開發程式。等程式寫好,你便可以跟老闆回報進度。

你:報告老闆,您要的功能我們開發好了。

老闆:我看看……嗯,沒錯,這就是我要的功能。什麼時候可以上線使用?

你:我們的部分已經沒問題了,但是客服部門還沒有完成他們的開放資料API,實際上線時間要問客服部門。

老闆:客服部門的人在嗎?!

透過相依反轉原則,你成功把球丟給客服部門,進度就不是卡在你們這邊了。

***

友藏內心獨白:等來等去等成仇。