l

2023年3月20日 星期一

使用ezSpec落實行為驅動開發與實例化需求(3):撰寫Scenario與傳遞簡單參數

March 18 20:27~22:12;March 20 00:00~00:30



前言

介紹完ezSpec的領域模型(請考<使用ezSpec落實行為驅動開發與實例化需求(1):領域模型介紹>)與Feature和Story(請考<使用ezSpec落實行為驅動開發與實例化需求(2):Feature與Story>),終於要進入主題,今天介紹如何用ezSpec撰寫Scenario。

***

撰寫Scenario

假設你要開發一隻開發票的程式,可以從未稅金額計算含稅總價與稅金,你幫這隻程式寫的第一個Scenario如圖1所示:「當一台電腦的未稅金額為20000,營業稅為5%,當你買了這台電腦,你應該支付含稅金額21000的總價,其中1000元為營業稅。」

產生Scenario的方式很簡單,透過feature.withStory()找出事先建好的Story(請考<使用ezSpec落實行為驅動開發與實例化需求(2):Feature與Story>),然後呼叫Story身上的newScenario就可以產生一個新的Scenario。在產生Scenario的時候你可以指定這個Scenario的名字,如果未指定則ezSpec會自動抓取test method的名字當作Scenario的名字。

產生Scenario之後,可以透過它的Given、When、Then、And、But(統稱為Step)等method來撰寫Scenario的實際內容。每一個Step接受兩個參數:

  • 字串:用來描述Step內容的文字敘述。
  • Lambda:實際實行Step的程式,ezSpec使用Lambda來取代Cucumber的Step Definition。

由於本系列文章的主要目的是介紹ezSpec的使用方式,並不是要介紹透過TDD/BDD/SBE來開發軟體,所以接下來Teddy會說明撰寫Scenario的時候如何指定參數、讀取參數,以及傳遞參數的方式。

 

▲圖1:ezSpec的Scenario範例

***

指定與讀取無名參數

在圖1的範例中,一共有四個參數:

  • 第61行的未稅金額20,000
  • 第64行的營業稅5%
  • 第69行的含稅總價21,000
  • 第72行的營業稅1,000

有兩個方法可以在Step的Lambda中讀取這些參數,第一種方法是在這些參數前面加上$符號,Step的Lambda可以傳入一個ScenarioEnvironment的物件當成參數,可以透過ScenarioEnvironment在Lambda中讀取$開頭的字串,請參考圖2中的env參數。

 

▲圖2:ezSpec指定與讀取無名參數的Scenario範例

 

圖2示範三種讀取參數的函數:

  • 第62行evn.getArg:回傳String型別的參數
  • 第65行evn.getArgd:回傳double型別的參數
  • 第70行evn.getArgi:回傳int型別的參數

透過$方式所指定的參數,只有value,沒有key,因此在讀取時需透過index方式來讀取資料。在圖2中每一個Step剛好都只有一個參數,因此透過env.getArg(0)就可以拿到這些參數。

***

指定與讀取有名參數

既然有無名參數,就有有名參數,請參考圖3:

  • 指定有名參數:參考圖3的61行可用 ${tax_included=20,000},或是用第64${vat_rate:5%}來指定參數的名稱(可用=或:)。
  • 讀取有名參數:參考圖3第62、65、70、73,讀取方式和圖2類似,但此時使用字串的key來讀取參數內容。

 

▲圖3:ezSpec指定與讀取有名參數的Scenario範例

***

讀取其他Step的參數

有時候你想要在Lambda中讀取其他Step所定義的有名參數,這時候就要用getHistoricalArg函數來取得,請參考圖4第74行:env.getHistoricalArg("tax_included") 讀到第61行所定義的tax_included參數。

 

▲圖4:ezSpec讀取其他Step的有名參數範例

***

在不同的Step中傳遞資料

在撰寫Scenario的時候經常需要在不同的Step之間傳遞資料,例如在67行的When當中你用hasBought變數代表成功購買到電腦,你想在Then的Lambda中驗證hasBought的內容。請參考圖5,此時可以使用ScenarioEnvironment的put(key, value)將要傳遞的資料放到ScenarioEnvironment(第69行),然後在另一個Lambda中用get(key, Class<T>)將資料讀出(第73行)。

 

▲圖5:ezSpec在不同的Step中傳遞資料

***

結語

今天介紹ezSpec撰寫Scenario與指定和讀取參數的方式,Scenario也可以接受一個Table的資料當作參數,Teddy將在下一集介紹這個功能。

***

友藏內心獨白:用Lambda撰寫Step Definition就不用處理煩人的正規表示式了。

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撰寫需求範例。

***

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

2023年3月14日 星期二

使用ezSpec落實行為驅動開發與實例化需求(1):領域模型介紹

March 14 18:10~19:02;20:28~23:23

▲圖1:ezSpec領域模型

 

前言

行為驅動開發(Behavior-Driven Development;BDD)與實例化需求(Specification by Example)是兩種主流的測試驅動開發方法。藉由開發人員與領域專家或利害關係人一起探討需求(協同建模),以及代表需求的具體範例,並將其表達在自動化測試案例中,以達到活文件(Living Documentation)的效果。

 

Teddy在開發ezKanban的過程中,在2021年順手作了ezSpec—它是用Java撰寫的Internal DSL,可以簡化使用Cucumber或JBehave等工具的麻煩(請參考<無痛將驗收測試文件寫在測試案例中>)。Teddy當初開發ezSpec是想要做Living Documentation的研究,後來ezKanban的事情太忙,做好ezSpec之後只有在ezKanban中使用它,一直沒有時間進一步把它做到完整的支援Living Documentation。這陣子因故又把ezSpec拿出來「積極開發」,等開發到一段落準備把它開源。在開源之前先寫幾篇文章來介紹ezSpec,日後也可當作ezSpec的使用文件。

***

ezSpec的領域模型與使用範例

圖1是ezSpec的領域模型,基本上ezSpec參考Gherkin語法所設計,差別在於ezSpec可以描述Story以及同步執行步驟。這些細節之後再詳細說明,先介紹領域模型各個物件的意義:

  • Feature:功能,代表可以提供使用者價值的單位。關於Feature的「粒度大小」有不同的用法,有人將「整個功能模組」當成一個Feature,然後再透過Story或Scenario切成比較小的功能。例如,把「結帳」當成一個Feature,裡面包含現金結帳、信用卡結帳、xxx Pay等。也有人把Feature當成一個單獨功能使用,粒度大小相當於傳統的使用案例(Use Case)。
  • Story:故事,就是敏捷社群經常使用的User Story。Gherkin/Cucumber本身並沒有支援Story,但JBehave有。有些人主張Story只是開發過程中用來「切割需求」的一種任務分派的單位,最後交付給使用者的是Feature。既然Gherkin是用來描述需求的一種語法,不需要使用Story去紀錄開發過程的「臨時性產品」。但許多傳統敏捷社群的人可能習慣用Story來做為需求溝通的最小單位,所以認為還是有Story比較好。ezSpec支援Story,一個Feature可以有一到多個Story。
  • Scenario:劇情,基本上一個Scenario可以想像成Specification by Example裡面的Example,透過舉例來溝通需求。一個Feature或Story具體來說到底要做什麼?用舉例的方式來表達,比較具體明白,也可以減少誤會。幫一個Feature或Story列舉出幾個具代表性的Scenario之後,團隊如果覺得已經可以瞭解這個功能的內容,這些例子就成為這個功能的驗收條件。如果可以將這些例子(也就是Scenario)變成程式自動執行,它們就成為自動化驗收測試。圖2為ezSpec的Scenario範例,由於ezSpec是一個Internal DSL(Domain Specific Language),使用ezSpec描述規格時就跟寫程式是類似的,直接把Gherkin的Given-Then-Then內容寫在lambda裡面。


▲圖2:ezSpec Scenario範例,pending()表示該Step尚未實作

 

圖3為ezSpec執行圖2的Scenario所產生的報表。


▲圖3:ezSpec產生的Scenario執行結果報表

***

  • Scenario Outline:劇情大綱,幫一個Feature或Story舉了幾個例子之後,如果發現這些例子的執行步驟都相同,只有輸入的資料與執行結果不同,就可以將這些個別的Scenario整理成一個Scenario Outline。Scenario Outline基本上就是傳統軟體測試中的「參數化測試」或是「資料驅動測試」,在Gherkin中,提供不同資料的方式是指定一組Examples。請參考圖4的ezSpec ScenarioOutline範例,範例表格資料內容參考自https://reurl.cc/qkrL9y。

 

▲圖4:ezSpec ScenarioOutline範例

 

圖4第160~177行是指定Examples的地方,兩組Examples一共以五筆測試資料。第179~187行新增一個Scenario Outline,整個Scenario Outline寫在JUnit 5 的test method內,執行結果與一個一般的測試案例相同,可以在JUnit報表中看到,如圖5。

▲圖5:Scenario Outline執行結果

 

ezSpec可以將整個Feature的執行結果產生文字報表,圖6為上述Scenario Outline執行結果所產生的報表,可以看到每一輪執行的輸入資料,以及每一個步驟(Step,請參考後面說明)的執行結果。


▲圖6:ezSpec產生的Scenario Outline執行結果文字報表

 

***

 

  • Runtime Scenario:Scenario在ezSpec是一個抽象類別,在執行期間,每一個Scenario,以及Scenario Outline展開之後每次執行都會產生一個Runtime Scenario物件,用來記錄輸入資料與執行結果。
  • Background:背景,請參考圖7,類似JUnit的@BeforeEach,可以將多個Scenario的共同步驟定義在Background中,然後再重複使用。Background雖然達到減少重複描述步驟的好處,但是會讓Scenario變得比較不容易閱讀,使用時要稍作取捨。


▲圖7:ezSpec Background範例


  • Step:步驟,一個Scenario可以包含多個Step。Given、When、Then、And、But這些都屬於Step的一種類型。
  • Given:用來描述執行功能的前置條件。
  • When:用來描述執行功能,也就是傳統測試所說的「執行待測程式」。
  • Then:用來驗證結果功能執行後的結果。
  • And, But:在Given、When、Then之後可以加上And與But作為接續步驟。
  • Concurrent Group:Gherkin的Step只支援循序執行,也就是你無法用Gherkin描述同步行為的規格。Teddy的指導教授鄭老師指導一組研究IoT的學生,用Python開發了一個稱為concurrentSpec的工具,可以使用原本Gherkin的語法來描述IoT系統地同步行為規格。原本ezSpec也只支援循序的Step,最近一個月才參考concurrentSpec,加上描述同步行為的能力。

***

 

結語

傳統使用Cucumber、SpecFlow或是JBehave,都是先用Gherkin撰寫純文字的feature file(或是 story file),再透過工具產生Step Definition Method/Function,然後開發人員負責實作這些Step Definition Method,把規格變成可執行文件。

這種方式,原本是希望領域專家或利害關係人可以直接用Gherkin採用舉例的方式描述需求,再交由開發人員將其轉成自動化驗收測試。概念很好,但這種方式有兩個很大的問題,首先領域專家或利害關係人大該都不會用Gherkin直接幫你寫規格,他們願意口頭跟你溝通、討論,而且是持續地溝通、討論,就已經很了不起了。其次,Step Definition Method很難寫,也不好閱讀與維護。因為要用到很多正規表示式將feature file所描述的內容傳給程式,要先學會不同工具對於Step Definition的繁瑣撰寫規範,實際使用時很容易出錯,讓開發人員無法專心在描述規格與範例上面。既然到頭來feature file與Step Definition都要開發人員自己寫,為什麼不用「開發人員友善」的方法與工具,來做BDD/SBE呢?

改用Internal DSL的方式,直接拿掉煩人的Step Definition與正規表示式,整個BDD/SBE/TDD流程變得很順暢。用Internal DSL的方式並不是Teddy發明的,有人用Ruby做過,只是Teddy一直到2021年在《Living Documentation: Continuous Knowledge Sharing by Design》書中看到一個類似的例子之後,才引發自己也用Java開發類似工具的念頭。

Teddy自己使用的結果表明,真的是方便很多。這一集先到這邊,之後再詳細逐一介紹ezSpec的每一項功能。

***

友藏內心獨白:軟體還沒Open,文件先Open。

2023年2月7日 星期二

與ChatGPT聊天(1)

February 07 20:57~22:15

▲ChatGPT官網首頁畫面

 

前言

ChatGPT自從去年11月推出以來引起很大的關注,原本Teddy對於這方面的議題是沒有涉略,但上周五在會議中聽鄭老師(Teddy的指導教授)分享他使用ChatGPT的經驗,引起Teddy對ChatGPT的興趣。

Teddy完全不懂AI,對於ChatGPT背後的運作原理也不了解。單純以軟體開發人員的身分,從軟體設計與學習的角度,分享Teddy使用ChatGPT的經驗。

***

幫我寫合約

最近這陣子Teddy與ezKanban團隊在研究以驗收測試加上Design By Contract(DBC)取代傳統驗收測試加上單元測試來確保軟體品質的方式,於是Teddy就想考考ChatGPT對於寫合約的能力。

 

Teddy請ChatGPT針對Stack class產生push method的合約,結果如下。如果不懂DBC的人,也無法判斷產出結果是否正確(廢話XD)。如果略懂DBC的人,可能會覺得ChatGPT產生的合約,已經與一般教科書中的參考答案相去不遠,頂多少一個stack not full的precondition。

 

***

 

但Teddy一眼就看出來,上面的程式少了一個很重要的postcondition,於是Teddy繼續追問:「postconditions好像不完整」。沒想到ChatGPT自己就補上了一條新的postcondition:

@post For all i in [0, list.size() - 2], list.get(i).equals(old(list.get(i)))

這一條postcondition想表達「除了stack top元素以外,stack內容在push之前與之後的其它元素要相等」,換句話說push只可以將新元素放到stack頂端,不可動到原本stack內部的其它元素。。

 

***

 

看到這裡,鄉民們覺得ChatGPT是不是很厲害?但請仔細看它產生的程式碼,其實有bug。什麼bug,Teddy再給它一個提示:「list.get(i).equals(list.get(i)) 不能確定現有元素沒有被改變,你必須儲存 old list」。

這一次,ChatGPT產生的程式「看起來」就算完整了。

 

試到這裡Teddy覺得很奇怪:很明顯地ChatGPT是知道push contract的「完整答案」,但為什麼第一次只給了一個常見但不完整的答案?是為了節省運算資源嗎?還是有什麼其他原因,就不得而知

 

***

老師在講你有沒有在聽?

正當Teddy以為ChatGPT已經「學會」如何回答這個問題,於是Teddy重新再問一次,而且強調「The generated contracts should be as accurate and correct as possible. 」,提醒它不要忘了第三條postcondition。

但是,ChatGPT依然故我,還是回答只有兩條合約的答案。

 

***

 

Teddy還是好心提問:「Is the postcondition correct?」但這次ChatGPT居然回答Yes。之前Teddy問:「postconditions 好像不完整」,ChatGPT就補上第三條合約,但問它「postcondition是否正確?」,可能它覺得這兩條postcondition是正確的,所以回就答Yes。

 

對話至此,這個問題Teddy的懶得繼續問下去了。

***

感想

如果你對於詢問ChatGPT的問題心中已有答案,可以分辨ChatGPT產生結果的正確性,那麼它不失為一種快速代替人類產出文字的好工具。但是如果你對於問題的答案沒個準,那麼盲目相信ChatGPT的產出結果是很危險的。因為它才剛剛問世,對於它產生的結果的「信心指數」還是個謎。如果你在教科書上學到一個知識,基本上信心指數很高,你可以基於這樣的知識繼續累積其他更大的知識體系,但現階段ChatGPT應該還是達不到這種等級的信心要求。

從Teddy的角度來看,經過與ChatGPT對話之後獲得正確答案,自然期望相同問題再問一次,就可以得到上次對話的結果,但ChatGPT卻沒有滿足這個期待。這也是另一個令人失望的地方,因為原本想把它「訓練」成自己的小幫手,但是訓練過後它又忘了,那就無法將使用者的個人知識累積在ChatGPT上面。

Teddy覺得平均而言,ChatGPT對於問題的理解能力與專業知識量應該已經狂勝個別的人類。只要提升ChatGPT產出結果的正確性,還有就是請它做到「知之為知之,不知為不知」,不要不懂裝懂,這樣子可用性就更大,更值得依賴它用來解決特定的小問題。

***

 

友藏內心獨白:受過專業的廢話訓練。

2023年1月4日 星期三

也許我不會刪除你:重複程式碼不等於壞味道

January 04 00:00~01:10


前言

今年一月上班沒幾天就要過農曆年,去年底在安排泰迪軟體2023年課程表的時候,索性就把整個一月空下來。本來想要找機會再溜出國玩,後來因為去年年底太忙,沒時間安排行程,乾脆把時間拿來在家裡讀書算了。

這幾天同時間讀了幾本書,其中有一本《Five Lines of Code: How and when to refactor》滿有意思的。這是一本討論Refactoring(重構)的書,有別於傳統的重構,書中採用Rule-Based Refactoring,藉由提供幾條非常具體的規則,讓開發人員知道何時以及如何重構。例如書中的第一條規則就是這本書的書名:每一個method的程式碼不可以超過5行(不包含括號)。

今天Teddy要談這本書提到的另一個重構中經常遇到的問題,就是Duplicated Code(重複程式碼)。傳統上認為重複程式碼是Bad Smell(壞味道、怪味道),應該要將其斬草除根。關於這一點,基本上沒什麼爭議。如果程式中有很多重複程式碼,一旦需求改變涉及到這些程式碼,開發人員就需要修改每一處的重複程式碼,否則系統就會發生錯誤。這就造成另一個壞味道:Shotgun Surgery。

***

再探重複程式碼

這幾年Teddy因為學習Clean Architecture與Microservices,發現透過重複程式碼來避免模組之間的依賴,反倒是一種很常見的方法。例如,Clean Architecture的跨層原則要求Entities Layer的物件離開Use Cases Layer時必須轉成另外一種資料結構;若是往前端傳遞則轉成DTO(Data Transfer Object),往資料庫傳遞則轉成PO(Persistent Object)。雖然DTO與PO與其所相對應的Entities Layer物件並不一模一樣,重複的部分只有資料,但廣義來看這也可以視為一種重複程式碼。


《Five Lines of Code: How and when to refactor》書中對這一點解釋的很好:

Sharing code increases global behavior-change velocity, while duplicating code increases local behavior-change velocity.

針對某個功能,如果全部程式碼都共用(沒有重複程式碼),那麼當系統行為改變的時候,只要改一個地方即可,因此「global behavior-change(全域行為改變)」的速度會很快。反之,如果同一個功能每次用到它的時候都將其複製一份,那麼這份複製出來的程式碼就與原本的「本尊」獨立,去除了耦合。在這種情況下,「local behavior-change(區域行為改變)」的速度就會很快。


從生物學的角度來看,獨立的區域,例如島嶼、沙漠或高山,經常會演化出「特有種」生物。一開始這些生物的起源是相同的,但是為了應付「區域性需求」,逐漸演化出不同的特徵。 程式碼也經常如此,因為被共用的程式碼在使用它的地方可能同時存在不同的區域性需求(書中稱為local invariants),此時如果堅持「共用」,很可能導致需要回頭修改共用程式碼,讓它具備適應不同區域性需求的能力(透過設定或依賴注入),因而增加共用程式碼的複雜度,最後可能會複雜到降低它的可讀性,因而反倒造成可修改性下降(原本共用希望提升global behavior-change速度,但在這種情況下,可能反倒降低global behavior-change速度)。

***

Shotgun Surgery的問題勒?

討論至此,Teddy也不是鼓吹盲目使用「複製、貼上」來去除模組之間的依賴這樣就好棒棒,「利用重複性去除依賴」這個方法,還是要從軟體架構與邊界這兩個角度來考慮。假設你發生重複性的邊界是在同一個method裡面,這種重複性幾乎可以斷定是壞味道,可以用Extract Method將其移除。如果發生重複性的邊界是同一個package,則也有很大的機會是壞味道。但是如Teddy前述的情況,在Clean Architecture的跨層原則之下,重複性的邊界已經是架構階層之間,此時使用重複性去除依賴爭議就比較小。

另一個常見的情況是在微服務架構中,下游微服務透過聽取上游微服務的事件,在本地端建立讀取模型(Read Model)以隔離兩個微服務之間的執行期間依賴。

 

***

結論

Teddy在開發ezKanban的過程中也遇到很多「是否要共用,還是利用重複性去除依賴」的設計決定,例如ezKanban支援Event Sourcing與State Sourcing這兩種狀態儲存方式,一開始ezKanban的報表先支援State Sourcing,然後再支援Event Sourcing。為了支援這兩種儲存方式,團隊在Repository的實作中套用Pluggable Adapter設計模式,整個設計簡單易懂。

等開發完成之後,團隊發現有不少報表除了撈資料的方式不同(一個下SQL另一個操作event streams),其餘產生報表的計算邏輯大致上是相同的。為了去除這些重複性,又花了不少時間重構系統。重構完成後,去除重複程式碼,但付出的代價就是設計變得更間接(因為多了一層抽象介面),也沒那麼直覺。

Teddy覺得這是一個有趣的議題,重新省視「程式碼共用」這件事,共用並不一定都是好的,想想你的中台…XD。

 

***


友藏內心獨白:To share, or not to share, that is the question。

2022年10月11日 星期二

重辦護照

Oct. 11 11:25~12:02

▲現場辦理護照的排隊人龍

 

疫情前Teddy和Kay每年至少會固定出國兩次,因為疫情緣故已經兩年多沒出國。最近政府宣布國境解封,護照在明年中即將過期,差不多要重辦護照。

上次辦護照已經是近10年前,記得是某天晚上等Kay下班後一起去外交部領事事務局辦理。幾天前上網查詢,發現申請新護照方便很多,只要先上網填寫資料,然後預約辦理的時間,到時候到外交部領事事務局一樓用機器報到,領取號碼牌之後到三樓窗口辦理即可。

詳細流程可參考:外交部領事事務局個人申辦護照網路填表及預約系統,或申辦護照流程短片。

***

護照申請流程網路上資料很多,Teddy就不在重複。這篇簡短分享今天早上去外交部領事事務局辦理辦理護照的幾個心得:

  • 報到時間:Teddy預約的時段是10:30~11:00,抵達現場的時間是10:25。經工作人員告知,可以直接在一樓的公共事務機報到領取號碼牌,不用等到10:30。


▲公共事務機長這樣,圖片節錄自申辦護照流程短片。

 

  • 可現場列印護照申請表格:在報到的同時可透過機器選擇列印護照申請表格。Teddy已經事先上網填寫資料並且在家裡用彩色印表機列印出申請表格,所以不需要重複列印表格。如果家中沒有印表的朋友,可以現場列列申請表(表格會帶出網路預約時所填好的基本資料,包含彩色照片,只要將表格印出來簽名即可)。但是這裡有一點要注意,如果你是幫別人(例如親屬)代辦護照,就無法現場列印表格,要在家裡印好,請委託者簽好名帶過來。
  • 過號:拿到號碼牌之後Teddy就立刻搭電梯到三樓,沒想到居然已經過號了,有點傻眼(有兩個網路預約的專門辦理窗口)。請問工作人員,對方很親切地說:「沒關係,你就排在後面,等前面一位辦好後跟工作人員說你過號就可以。」在辦理的過程中,發現也有其他人遇到過號的問題,所以這可能是….正常現象吧。
  • 辦理時間:從抵達現場到辦好護照手續、請郵局寄送護照,一共花了15分鐘,算是非常快了。但是,有很多民眾是現場辦理,沒有網路預約,排隊的人潮就很多。

***

現場排隊的人真的很多,Teddy預估至少要等一小時吧。在離開時Teddy就聽到有已經抵達現場的民眾說:「我們回家網路預約,之後再來好了。」如果不想等太久,網路預約方便很多。

***

友藏內心獨白:難得覺得政府系統做得還不錯。

2022年8月25日 星期四

順序很重要

August 25 17:43~18:21


 

前言

這兩年多和ezKanban團隊一起mobbing,後端套用了Domain-Driven Design(DDD)、Clean Architecture、Event Sourcing、CQRS、TDD、Design By Contract(DBC)、Living Documentation,幾乎全部的後端架構一開始都是Teddy設計的。這原本就是正常現象,因為ezKanban是Teddy帶著學生一起開發的軟體研究專案。

有時候Teddy需要上課無法參加mobbing,課程結束回來之後發現Teddy不在的時候團隊快速完成了一些功能,但是設計方法不是很好,於是Teddy會跟團隊討論一次設計,再看看要如何重購。

***

順序

Scrum有三種角色,Product Owner(PO)、Developer、Scrum Master(SM),用一句話來形容他們的責任分別是:

  • PO:Do the right thing(做對的事情)
  • Developer:Do the thing right(把品質做好)
  • SM:Do it faster(持續改善)

這三件事情,發生的順序是有意義的。首先,要先確保Do the right thing,要了解問題領域中使用者、客戶遭遇到什麼問題?其次,設計出好的軟體來解決這些問題。最後,持續改善整個開發流程。

許多開發人員非常在意電腦是不是最新的,IDE的熱鍵是否用得滾瓜爛熟。這兩件事情,都重要,也都可以縮短開發時間。但是有一件事情比它們還重要,就是你要先確定你做的事情是對的。你的需求、架構、設計,都是對的,否則只是把錯誤的事情做得更快。

***

 

迭代

上述這三件事,它們發生與實踐的過程,並不是線性的,而是迭代的過程。也就是說,先了解需求一點點,然後做點設計、寫點程式,然後改善;然後重複這個過程。Teddy和團隊mobbing的過程,首先強調的一定都是需求,為什麼要做這個功能?先確認需求,然後才討論領域模型與架構要如何滿足這個需求。至於開發流程的改善,工具類只占一部分,它是很重要的一部分但不是最重要的部分。Teddy只會要求IDE基本的功能與快捷鍵要熟悉,開發環境、測試環境、部屬環境儘量自動化與虛擬化。Mobbing的時間,Teddy在意的流程改善,是能不能提升團隊的設計力、讓大家動腦、可以清楚表達一件事。至於IDE熟悉度的提升如果要達到完全不需要使用滑鼠的程度,有興趣的人私底下再自行練習即可。

雖說這三件事是以迭代的情況發生,但還是要注意順序,不要一開始只注意do it faster,也不管打進去IDE裡面的東西到底是什麼,那就本末倒置了。

***

 

友藏內心獨白:垃圾進,垃圾出。