在 LeSS 最後一天,聽到別的公司在導入敏捷時,也成立了很多很多的實踐社群 CoP (Community of Practice),其中一個是 心靈成長 CoP,讓我印象非常深刻,這邊來聊聊為什麼 :)
在第五項修練中,彼得聖吉一直在強調"整體":
「遠古的人類並未把自己跟所處的世界加以區分。那時的人類所看見的世界是一個未被打破的整體,人與自然合而為一。但不知自何時起,我們學會了區分自己,
視自己為分離的個體。我們刻意凸顯個人意識,強調獨立的意志、個人需求和個人的願望。這種自我意識的演化愈來愈強,我們也愈來愈與他人以及上帝所創造的萬物區分。
這對人類的演進而言,是福,也是禍。」
「在企業裡,行銷部門與製造部門處於對立狀態;第一線的管理人員對總公司管理當局懷有近乎憎惡的敵意;各部門的競爭更甚於跟同業的競爭。」
今天上 LeSS,沒想到這邊的 Large Scale Scrum 已經發展到這麼成熟了...果然是連車尾燈都看不到。今天的同學,大部份都有豐富的LeSS相關經驗,也有搞 SAFe 的,也有 Scrum coach,真的是令小弟無地自容..
這裡頭的故事,就讓我娓娓道來..
------------------------------------------------------
去年二月上完呂毅老師的 CSM 之後,對呂毅老師的授課方式及內容印象很好,然後公司內的 Scrum 團隊也開始在增加,因此當老師寄信來說要開 LeSS 的課程時,就毅然決然的報名了。
這次的課程在 Odd-e上海辦公室(隨著上海房價的飆漲,應該叫 Odd-億上海豪宅 才對) ,就在世紀公園正對面,我挑了最近的四星酒店[帝盛酒店],地鐵2號線[世紀公園站]出來就到,走路到豪宅上課也只要10分鐘,推薦給大家,下次去上課,訂這間就對了 XD
早上的課程一開始,先是同學們的自我介紹&你想學到什麼?,總共有11位學生,只有我一位是從台灣過來,剩的大部份都是從杭州過來。大部份同學的產品都是硬體產品,少數是純軟的,因為公司規模都很大,所以大部份都已經是 LeSS-like 的架構在運作,而且應該已經運作好幾年了..
自我介紹完,老師就開始講系統分析,這次 LeSS 的課程,重點不在講 LeSS 要遵循的那些規則,因為那就看書照著作就好了,老師反而是帶我們了解背後的原因(我覺得這是呂毅老師一貫的上課風格,就是投影片其實都很快就帶過,都在講投影片沒寫,更深層的一些想法)。
一個還是多個 product backlog ?
一個還是多個 PO?
一個還是多個 Scrum Master?
Feature Team or Component Team?
一個還是多個 PO?
一個還是多個 Scrum Master?
Feature Team or Component Team?
LeSS 在很多選擇中,做出了決定,而形成了 LeSS,為了了解這個決定背後的道理,老師先介紹了系統分析方法(CLD Causal-Loop Diagram):
Variable:變量
Link:連結
Loop R-loop & B-loop 增強迴路及平衡迴路,可見[第五項修練]&[系統之美]
而在二天的課程當中,我們總共實際演練了4次CLD,我覺得這點很重要,因為要做到[守破離],唯有洞悉背後的因素,才能到破,然後到離。LeSS(或著就稱大規模Scrum好了)在實際運作時,一定會碰到各式各樣的狀況,這時要如何調整團隊及運作方式,系統分析就很重要了。
Variable:變量
Link:連結
Loop R-loop & B-loop 增強迴路及平衡迴路,可見[第五項修練]&[系統之美]
而在二天的課程當中,我們總共實際演練了4次CLD,我覺得這點很重要,因為要做到[守破離],唯有洞悉背後的因素,才能到破,然後到離。LeSS(或著就稱大規模Scrum好了)在實際運作時,一定會碰到各式各樣的狀況,這時要如何調整團隊及運作方式,系統分析就很重要了。
講到這,提一下這次看到的:有些公司的 Scrum Team 沒有 Scrum Master(好,你也可以說這不能叫Scrum Team),有些也不一定都是Feature Team,有些會摻些 Component Team,有些自己添加了一些 roles 來處理溝通協調,有些公司處理bug是由專門團隊負責,也有一定是找回原作者的..,有些 team 竟然有所謂的"組長"..有些人有 release sprint,有些人有獨立的 testing team 來平行進行測試。
非常多的變體,所以這次課程中一個很大的收穫是,了解這些變體背後的原因,然後,我們這種才從[守]開始的,對於他們組織改變、心智模式改變已到[破、離]的速度,感到震撼,而且,他們都是很巨大的公司,也有很老的公司,還能這麼有魄力的改變,真的很厲害。最厲害的是,這些都是枱面上很成功的公司,一聽到就會 哇 的那種成功公司..
非常多的變體,所以這次課程中一個很大的收穫是,了解這些變體背後的原因,然後,我們這種才從[守]開始的,對於他們組織改變、心智模式改變已到[破、離]的速度,感到震撼,而且,他們都是很巨大的公司,也有很老的公司,還能這麼有魄力的改變,真的很厲害。最厲害的是,這些都是枱面上很成功的公司,一聽到就會 哇 的那種成功公司..
轉回CLD,為了讓我們熟悉CLD,老師用了[狼圖騰]裡頭的故事讓我們練習,簡單說:
Variable:狼,野物,草場,牛羊馬,設備
Link:狼會吃野物&牛羊馬,野物會破壞草場,草場可以養育牛羊馬,牛羊馬帶來錢,有錢可以買好設備,好設備可以讓草場更好
Loop:上面這些 Link 可以形成各種 R-loop & B-loop ,因此我們在這個 CLD 中,就可以想看看,如果希望牧場的利益最大化,應該要怎麼做最好?也可以看出來那些作法是"飲鴆止渴",那些作法是"長久之計"。
Variable:狼,野物,草場,牛羊馬,設備
Link:狼會吃野物&牛羊馬,野物會破壞草場,草場可以養育牛羊馬,牛羊馬帶來錢,有錢可以買好設備,好設備可以讓草場更好
Loop:上面這些 Link 可以形成各種 R-loop & B-loop ,因此我們在這個 CLD 中,就可以想看看,如果希望牧場的利益最大化,應該要怎麼做最好?也可以看出來那些作法是"飲鴆止渴",那些作法是"長久之計"。
這時再回來看,大型 Scrum 是要 1 Product Backlog 或著 多個 Product Backlog 就很有趣了
:)
就在這一堆CLD的討論中,結束了早上的課程。
中午老師帶我們到附近吃山西菜,山西菜份量多又紮實,吃完一餐就胖了一圈 XD 分享一下午餐故事,做為這一集的 ending
--
同學A:還多一塊羊排呀,誰把它夾掉吧
同學B:台灣的同學比較沒吃過這味道,給台灣的同學吧~
Diro:啊,我們台灣也是有羊的啊!!(但我還是吃了.......)
同學C:這個韭菜盒子也多一個啊~~
Diro:耶,我告訴你,我們台灣也有韭菜盒子,不要再叫我吃啦
--
[某大企業的HR打來]
HR:我們這裡缺 資深測試工程師 ,你有沒有興趣呀?
同學B:耶,你怎麼判斷我很會測試的呢?
HR:我看你的技能上有個 Test-driven Development 的經驗
同學C: 上次我們QA部門也很緊張的跑來找我
QA:聽說你們現在要弄什麼測試驅動開發,先跟我們交流交流吧
同學C: 這跟你們QA可能沒什麼關係..
QA:這上頭明明就寫著測試兩個字,你不要再騙我了..
--
同學A:你們 Scrum 跑多久了呀?
Diro:二年多,應該是2014吧~
同學A:2014?(驚),那真的還蠻慢的呀..
--
下午的課程,因為吃太飽,我就睡著都沒聽到了...
--
同學A:還多一塊羊排呀,誰把它夾掉吧
同學B:台灣的同學比較沒吃過這味道,給台灣的同學吧~
Diro:啊,我們台灣也是有羊的啊!!(但我還是吃了.......)
同學C:這個韭菜盒子也多一個啊~~
Diro:耶,我告訴你,我們台灣也有韭菜盒子,不要再叫我吃啦
--
[某大企業的HR打來]
HR:我們這裡缺 資深測試工程師 ,你有沒有興趣呀?
同學B:耶,你怎麼判斷我很會測試的呢?
HR:我看你的技能上有個 Test-driven Development 的經驗
同學C: 上次我們QA部門也很緊張的跑來找我
QA:聽說你們現在要弄什麼測試驅動開發,先跟我們交流交流吧
同學C: 這跟你們QA可能沒什麼關係..
QA:這上頭明明就寫著測試兩個字,你不要再騙我了..
--
同學A:你們 Scrum 跑多久了呀?
Diro:二年多,應該是2014吧~
同學A:2014?(驚),那真的還蠻慢的呀..
--
下午的課程,因為吃太飽,我就睡著都沒聽到了...
ODD-E LESS@上海 心得&攻略 PART 1
最近因為工作的關係,很需要從 USB 不斷的開機。在實體的機器,開機需要等待兩分鐘以上的 BIOS 過程,生命不斷的流逝下,興起了採用 VM 進行測試的念頭。剛好最近買了一台新的 notebook,記憶體特意買大一點,所以就開始試起 VM boot。
過去同事們最常使用的是 VM Ware,所以先說說 VMWare ,簡單的答案是,VMWare 沒有支援從 USB 開機。但是有好心人寫了一套 plop boot manager,只要在 VMWare 中選擇建立一個以 iso 為開機的新 vm,其中的 iso 可以指定從 plop 的 zip 中取出的 plpbt.iso檔。然後當你 boot 時,就可以看到 usb 開機的選項。選了就可以從 usb 開機。但這個方法我用了身邊的所有 usb 碟,一直沒有成功 ,Clonezilla 可以,但 Windows embedded 7 的安裝 usb 就是不行 (FAT32 or NTFS 皆不行),我得到的畫面如下
大概就是說找不到可以 boot 的 device。另一個問題是 VMWare 的字在我的 notebook 上實在太小了,為了眼睛,我就跳槽到 virtual box 的懷抱了。
VirtualBox 是 Oracle 的 Open Source project,所以免費。另外他支援 VMWare 的 vmdk 檔案,這代表過去我們已經用了多時的 vm 基本上都可以繼續使用。不過小地方可能要注意,那就是兩個 vm 系統模擬的硬體可能會有些差異,所以一些 driver 可能得重裝。
進入正題,VirtualBox 有沒有支援 USB 開機呢? 答案是:也沒有。因為開機能不能選 USB 是 BIOS 的事情,所以簡單的說就是 VirtualBox 的 bios 並沒有提供 USB 開機,我看到有人在 virtualbox 的論壇上提到此事,開發人員的回應是,這樣的工程相當浩大,目前有更重要的事情是讓 vm 內 USB 3.0 的速度不要插實體的太多。聽起來是有點道理,但,我的大計不就報銷了 Orz。
不過幸好請教G大神後,找到很多人教你怎麼讓 VirtualBox 可以用 USB 開機,其中最多人教的做法是:利用 VirtualBox 的 command line 命令 VBoxManage internalcommands createrawvmdk -filename c:\usb.vmdk -rawdisk \\.\PhysicalDrive# 產生一個 raw disk,然後將這個 raw disk 加到 virtualbox 的 vm 內。記得這個 disk 看起來像 hard disk,所以一定要跟原來 vm 內的 hard disk 在同一個 storage controller 內 (一般來說是 SATA)。而且這個 disk 一定要是編號最小的一個,可以將原來硬碟的編號改大,再將 usb.vmdk 改到最小。
這樣就可以開機了。但是,我照做之後,得到的畫面是
看起來結果跟 VMWare 差不多(Clonezilla 也是可以)。後來猜想應該是 file system 的問題,所以費了九牛二虎之力 (中間的故事也很黯然銷魂,不過就不贅述了),終於讓我可以從 USB 開機了,所以 VirtualBox 的 bios 看來無法支援 NTFS 的 USB 開機,不過高興只有一下下,就又被塞了這個訊息
幸好 G 大神根據錯誤訊息給了一個指引:Virtual Machine USB Boot。
下載,免安裝,一切 OK,真是太完美了。Virtual Machine USB Boot + Virtual Box 看來將會是我最近的好幫手。
過去同事們最常使用的是 VM Ware,所以先說說 VMWare ,簡單的答案是,VMWare 沒有支援從 USB 開機。但是有好心人寫了一套 plop boot manager,只要在 VMWare 中選擇建立一個以 iso 為開機的新 vm,其中的 iso 可以指定從 plop 的 zip 中取出的 plpbt.iso檔。然後當你 boot 時,就可以看到 usb 開機的選項。選了就可以從 usb 開機。但這個方法我用了身邊的所有 usb 碟,一直沒有成功 ,Clonezilla 可以,但 Windows embedded 7 的安裝 usb 就是不行 (FAT32 or NTFS 皆不行),我得到的畫面如下
大概就是說找不到可以 boot 的 device。另一個問題是 VMWare 的字在我的 notebook 上實在太小了,為了眼睛,我就跳槽到 virtual box 的懷抱了。
VirtualBox 是 Oracle 的 Open Source project,所以免費。另外他支援 VMWare 的 vmdk 檔案,這代表過去我們已經用了多時的 vm 基本上都可以繼續使用。不過小地方可能要注意,那就是兩個 vm 系統模擬的硬體可能會有些差異,所以一些 driver 可能得重裝。
進入正題,VirtualBox 有沒有支援 USB 開機呢? 答案是:也沒有。因為開機能不能選 USB 是 BIOS 的事情,所以簡單的說就是 VirtualBox 的 bios 並沒有提供 USB 開機,我看到有人在 virtualbox 的論壇上提到此事,開發人員的回應是,這樣的工程相當浩大,目前有更重要的事情是讓 vm 內 USB 3.0 的速度不要插實體的太多。聽起來是有點道理,但,我的大計不就報銷了 Orz。
不過幸好請教G大神後,找到很多人教你怎麼讓 VirtualBox 可以用 USB 開機,其中最多人教的做法是:利用 VirtualBox 的 command line 命令 VBoxManage internalcommands createrawvmdk -filename c:\usb.vmdk -rawdisk \\.\PhysicalDrive# 產生一個 raw disk,然後將這個 raw disk 加到 virtualbox 的 vm 內。記得這個 disk 看起來像 hard disk,所以一定要跟原來 vm 內的 hard disk 在同一個 storage controller 內 (一般來說是 SATA)。而且這個 disk 一定要是編號最小的一個,可以將原來硬碟的編號改大,再將 usb.vmdk 改到最小。
這樣就可以開機了。但是,我照做之後,得到的畫面是
看起來結果跟 VMWare 差不多(Clonezilla 也是可以)。後來猜想應該是 file system 的問題,所以費了九牛二虎之力 (中間的故事也很黯然銷魂,不過就不贅述了),終於讓我可以從 USB 開機了,所以 VirtualBox 的 bios 看來無法支援 NTFS 的 USB 開機,不過高興只有一下下,就又被塞了這個訊息
幸好 G 大神根據錯誤訊息給了一個指引:Virtual Machine USB Boot。
下載,免安裝,一切 OK,真是太完美了。Virtual Machine USB Boot + Virtual Box 看來將會是我最近的好幫手。
Virtual Machine 的 Guest 怎麼從 USB 開機?
2/22 與 2/23 兩天去參加了一個外訓課程 - Advance Scrum Master。這個課程主要是給擔任過 scrum master 的人,想要提升自己的能力,或者組織要跑跨團隊產品的 scrum 的人聽得。我雖然沒有擔任過 scrum master,不過後面的那的目的,倒是即將在我們的單位內發生,因為我們在 Q2 開始,有一個專案會加入新的團隊成員,所以該專案的總人數已經超過一個 scrum team 的建議範圍,因此我們有必要開始注意跨團隊開發同一個產品的作法該怎麼進行。我在這裡借用一些篇幅,紀錄一下課程的收穫。
2007 年左右 Nokia 進行了一個 500 人左右的大型 scrum pilot,這就是後來 LeSS 的主要參考專案。跨團隊跑 scrum 有兩個主要門派,一個就是剛剛說的 LeSS,另外一個就是 SAFe。LeSS 叫做 Large Scaled Scrum,SAFe 則是 Scaled Agile Framework。比較上來說
LeSS 真的是比較 Less,而 SAFe 則是比較 Safe。聽起來好像甚麼都沒說對吧! 所謂比較 Less 是指在架構上,Less 跟跑一個團隊的 scrum 類似,幾乎不需要增加甚麼新的角色與流程,所以他的 Less 可以說是與 SAFe 的比較。而 SAFe 雖然增加了新的角色、流程、協作,但是這些新增的東西反而跟傳統開發模式比較容易接軌,例如 Architect 與 PMO 這些傳統瀑布下的角色在 SAFe 下反而會存活下來,所以他 Safe 的地方是對於一個跑慣傳統流程的公司來說,反而衝擊比較小。反過來說 LeSS 就會對傳統流程的公司是屬於破壞性的改變,所以才會說 SAFe 比較 Safe。但是 LeSS 的做法也會遇到比較難適應更大型的團隊,所以 LeSS 還有一個叫 LeSS Huge 的架構,基本上就是讓一定程度的人數組成一個 Requirement Area,這個 Requirement Area 相當於 LeSS,只是 Requirement Area 的 area product backlog 組起來還不能稱為產品,只是產品的一部分,所以會增加一個管理 Requirement Area 的架構。但那個管理架構本身也跟 scrum 類似,所以所有的設計理念都圍繞在scrum 本身。
LeSS 的做法,其實就是讓 PO 與 scrum master 可以管理多個 team,除此之外,所以的作為都跟原來的 scrum 一樣,除了在 planning 時有分上下半場,上半場由 PO 講解這個 sprint 的功能,並由各 team 領取 story,下半場則是各個 team 進行 story 的討論與切分,跟點數的評估。
而 SAFe 的做法是引進一個新的概念叫 Agile Release Train。Release
可以看成是由幾個 sprint 所組成,例如 5 個 sprint 加上一個整合的 sprint 組成一個 Release。所以各團隊的 sprint 可以不同步,但在 Release 前那個 sprint 要兜在一起。這個 Release 有個專有名詞叫 Program Incremental (PI),而那個整合的 sprint 則叫做 Hardening Innovation Planning (HIP),這個原來是進行整合並且 plan 下一個 PI 的機會,Hardening 有淬鍊的意思,不過後來因為大家覺得用 Hardening 似乎有鼓勵大家到這個 sprint 才進行整合,所以就把 Hardening 拿掉,稱為 IP 了。SAFe 成三層,最底下是 TEAM,也就是原來的 scrum team,只是多了剛剛說的 IP sprint,另外因為每個 team 看到的不是整的 product,所以稱 backlog 為 team backlog。第二層叫 Program 層,就是在這層定義 PI,這層的人稱呼也不同,原來的 PO 叫做 Business Owner,另位會有 Product Management 將 BO 整合在一起。最上層叫做 Portfolio,Architect 就放在這一層,也有 Epic Owner 負責大項目功能的定義,這一層會跟原來公司管理層銜接,所以會有 Budget、Stretagy 等在內。看完是不是覺得 SAFe 真的有點複雜! 我們在公司內目前如果遇到跨兩個以上 team 時,應該會以 LeSS 為主要的實作方式。
2007 年左右 Nokia 進行了一個 500 人左右的大型 scrum pilot,這就是後來 LeSS 的主要參考專案。跨團隊跑 scrum 有兩個主要門派,一個就是剛剛說的 LeSS,另外一個就是 SAFe。LeSS 叫做 Large Scaled Scrum,SAFe 則是 Scaled Agile Framework。比較上來說
LeSS 真的是比較 Less,而 SAFe 則是比較 Safe。聽起來好像甚麼都沒說對吧! 所謂比較 Less 是指在架構上,Less 跟跑一個團隊的 scrum 類似,幾乎不需要增加甚麼新的角色與流程,所以他的 Less 可以說是與 SAFe 的比較。而 SAFe 雖然增加了新的角色、流程、協作,但是這些新增的東西反而跟傳統開發模式比較容易接軌,例如 Architect 與 PMO 這些傳統瀑布下的角色在 SAFe 下反而會存活下來,所以他 Safe 的地方是對於一個跑慣傳統流程的公司來說,反而衝擊比較小。反過來說 LeSS 就會對傳統流程的公司是屬於破壞性的改變,所以才會說 SAFe 比較 Safe。但是 LeSS 的做法也會遇到比較難適應更大型的團隊,所以 LeSS 還有一個叫 LeSS Huge 的架構,基本上就是讓一定程度的人數組成一個 Requirement Area,這個 Requirement Area 相當於 LeSS,只是 Requirement Area 的 area product backlog 組起來還不能稱為產品,只是產品的一部分,所以會增加一個管理 Requirement Area 的架構。但那個管理架構本身也跟 scrum 類似,所以所有的設計理念都圍繞在scrum 本身。
LeSS 的做法,其實就是讓 PO 與 scrum master 可以管理多個 team,除此之外,所以的作為都跟原來的 scrum 一樣,除了在 planning 時有分上下半場,上半場由 PO 講解這個 sprint 的功能,並由各 team 領取 story,下半場則是各個 team 進行 story 的討論與切分,跟點數的評估。
而 SAFe 的做法是引進一個新的概念叫 Agile Release Train。Release
可以看成是由幾個 sprint 所組成,例如 5 個 sprint 加上一個整合的 sprint 組成一個 Release。所以各團隊的 sprint 可以不同步,但在 Release 前那個 sprint 要兜在一起。這個 Release 有個專有名詞叫 Program Incremental (PI),而那個整合的 sprint 則叫做 Hardening Innovation Planning (HIP),這個原來是進行整合並且 plan 下一個 PI 的機會,Hardening 有淬鍊的意思,不過後來因為大家覺得用 Hardening 似乎有鼓勵大家到這個 sprint 才進行整合,所以就把 Hardening 拿掉,稱為 IP 了。SAFe 成三層,最底下是 TEAM,也就是原來的 scrum team,只是多了剛剛說的 IP sprint,另外因為每個 team 看到的不是整的 product,所以稱 backlog 為 team backlog。第二層叫 Program 層,就是在這層定義 PI,這層的人稱呼也不同,原來的 PO 叫做 Business Owner,另位會有 Product Management 將 BO 整合在一起。最上層叫做 Portfolio,Architect 就放在這一層,也有 Epic Owner 負責大項目功能的定義,這一層會跟原來公司管理層銜接,所以會有 Budget、Stretagy 等在內。看完是不是覺得 SAFe 真的有點複雜! 我們在公司內目前如果遇到跨兩個以上 team 時,應該會以 LeSS 為主要的實作方式。
Advance Scrum Master - LeSS & SAFe
起因
Diro 分享了一個影片
以下簡單記錄我看完的心得
內容簡介
Speaker 是 Brian Lonsdorf (CTO of Loop/Recur, 多麼 geek 的公司名 XD)
LinkedIn: https://www.linkedin.com/in/drboolean
GitHub: https://github.com/DrBoolean
整個影片的重點在說明 Underscore 這個 library 雖然宣稱提供了一些有用的 functional programming helpers, 但是它的介面設計讓使用的人很難寫出真的 functional language 特性的 codes
Currying
以下他用幾個例子來說明 Underscore 的問題要看以下他舉的例子前, 先說明一個東西 - Currying
假設你有一個 function 叫 add
var add = function(x, y) {
return x + y;
}
假設你希望可以這樣寫var add2 = add(2); // return: function()
add2(10); // return: 12
add2(5); // return: 7
那你就要修改一下你的 add 定義var add = function(x) {
return function(y) {
return x + y;
}
}
var add2 = add(2); // return: function(y) {...}
add2(10); // return: 12
add2(5); // return 7
有更 generic 的作法, 請參考 http://blog.rx836.tw/blog/javascript-currying/作者也有提到一個幫你把你的 function 轉成 curried function 的 library (wu.js)
所以假設這樣寫會把你的 function 轉成 curried function\
var add = function(x, y) {
return x + y;
}.autoCurry();
舉幾個實際點的例子 (畢竟 add 有點沒感覺)var modulo = function(divisor, dividend) {
return dividend % divisor;
}.autoCurry();
modulo(3, 9); // return: 0
var isOdd = module(2);
isOdd(6); // return: 0
isOdd(7); // return: 1
var filter = function(f, xs) {
return xs.filter(f);
}.autoCurry();
filter(isOdd, [1, 2, 3, 4, 5]); // return: [1, 3, 5]
var getTheOdds = filter(isOdd);
getTheOdds([1, 2, 3, 4, 5]); // return: [1, 3, 5]
講者提的範例
- 範例一
var firstTwoLetters = function(words) {
return _.map(words, function(word) {
return _.first(word, 2);
});
};
firstTwoLetters(['jim', 'kate']); // return: ['ji', 'ka']
因為 Underscore 的參數順序是先 array 再 function所以沒辦法寫得更好
他認為如果 Underscore 能把參數順序顛倒且 API 是 curried functions
就可以寫成這樣
// _.first(n, array) 參數顛倒
var firstTwoLetters = function(words) {
return _.map(words, _.first(2));
};
// 更進一步, _.map(f, array) 參數顛倒
var firstTwo = _.map(_.first(2));
filterTwo(['jim', 'kate']);
- 範例二
var sortedPhones = function(users) {
return _.chain(users)
.sortBy(function(user) { return user.signup_date; })
.map(function(user) { return user.phone; })
.value();
};
sortedPhones(users);
他將其改寫為var sortedPhones = _.compose(
_.map(function(user){ return user.phone; }),
_.sortBy(function(useR){ return user.signup_date}));
sortedPhones(users);
更進一步var dot = function(prop, obj) {
return obj[prop];
}.autoCurry();
var sortedPhones = _.compose(_.map(dot('phone')),
_.sortBy(dot('signup_date')));
sortedPhones(users);
結論
我個人認為, 看完這個影片比看 Functional Javascript 這本書還更能感受 functional language 的特性 XD不過我也還不是很懂, 大家就一起學習吧
Hey Underscore, You're Doing It Wrong! 觀後感
https://blog.qt.io/blog/2013/04/15/evolution-of-the-qml-engine-part-1/
這一系列 QML engine 的文章(其實目前也只有一篇....)深入探討了 QML engine 的內部運作機制。Lars Knoll 指出了目前 QML engine 比較大的問題包括:
- Several object models
- 也就是一個 QML item 必需有3個object models,分別存在於 V8 engine, QML engine, Native Qt (以QObject 形式存在),不但耗費很多記憶體,要 sync 三個物件的值也是一大工程
- Bindings done through JS closures
- 每個 property binding 都是一個 JS closure,因此要先重新把 binding expression 重組成 JS closure,然後 V8 再 parsing 一次這個 closure。很慢,因此提出了一個輕量、快速的 V4 engine 來幫忙處理簡單的 expression(這也是為什麼官方的 performance tuning 文件叫你要盡量簡單化 binding expression)。
- Data type conversions
- Qt 跟 V8 之間的 data type 轉換很花時間,此外如果是 string 的話,memory copy 的成本也不低。當然,有些改善方法,但那就是拿記憶體或程式複雜度換來的了
- QML scoping rules
- QML element 實際上是樹狀結構,跟傳統的 JS engine 其實不大一樣,QML engine 用了很複雜的方法來處理這段(through nesting slow and deprecated with() statements)。
- Throwing away QML type info
- type info 會被丟棄..
- iOS and WinRT support
- V8要求系統記憶體可以被設定成 executable,但這在iOS及WinRT是做不到的...所以只能另外做一套 JS engine 了..
- Working with upstream V8
- Qt 裡面的 V8 跟標準版的 V8 程式碼已經差很多了..而且好像很難 merge 了...
Qt/QML Performance Tuning #1
在 Scrum Team 中很強調 透明(transparency),這次跟大家分享的是 Developer Resource 透明化帶來的好處。
傳統團隊
在傳統的開發方法上,常常都是以”月”、”季”為時間單位,因此常會聽到 PM 說:可不可以再加這個 X 功能、這個 Y 功能,還有那個 Z 也順便加進來好了,這一季完成這些,應該沒問題吧?
身為 developer 能拒絕他嗎?有點難,因為感覺一季的時間應該可以做蠻多事情的,就算你覺得不可以,PM也很容易這樣感覺:一季耶,三個月你竟然沒辦法幫我多做 X+Y+Z?
在 Developer Resource 不透明的的狀況下,還真的很難回答。而不透明的原因,其實是因為週期太長、功能多且不明確,倒不是因為什麼溝通或互信的關係。
敏捷團隊
但在敏捷團隊中,我們在每個 Sprint Planning Meeting 時,便會先算出這個 sprint 有多少資源可以用:6(hours) * 6(developer) * 8(days) = 288 points
接著在 Story 介紹、Story Point 估算、決定 priority 等等活動之後,便開始討論 PO 這次最後決定的 stories:A,B,C,D,E,F,把這 6 個 stories 再細拆分成各個 tasks,例如 Story A 可能會被拆分成下列 tasks:
- QML:這個功能有 UI 介面,需要實作 QML
- C++:實際功能由 C++ 完成
- Unit Test:要對 C++ 實作單元測試
- UAT:要完成自動化驗收測試
- Code Review:這次的 C++ 還蠻難的,雖然有 Unit Test,但還是要進行 Code Review
然後,接下來團隊成員根據每一項 tasks 開始打牌,決定每一項的 tasks 所需的時間。這裡是關鍵,因為要由這過程去弄清楚 PO 真正想要的東西,也讓 PO 了解這個 task 為什麼需要這些時間來開發。
以 C++ 這項 task 來看,如果一開始大家出的點數差異有點大,例如:3,3,3,5,20,3,5,便要請出3及20的成員們分別說明一下他認為是3及20的原因。
張飛(RD):我覺得這個 雙向語音 很簡單,用原本單向語音模組來改就可以了!
關羽(RD):可是他不是全雙工嗎?這樣我們要改很多地方才能做全雙工..
劉備(PO):耶,不用考慮全雙工,我們這個應用基本上只要雙工就可以了!
關羽(RD):原來如此,是我想太多了..
在一番討論後,大家才能真正清楚 PO 想要的東西,而不會多做或少做,或著方向不對。此時再出一次牌,通常就能取得共識了。
在經過撲克牌大賽後,可以得到每個 tasks 需要的時間:QML 5, C++ 20, Unit Test 20, UAT 20, Code Review 8,合計73小時。
剩餘的 Story B ~ Story F 也是照一樣的方式去得到需要的時間,可能是 B:80, C:100, D:64, E:38, F:48,這樣 PO 就很清楚,在這個 sprint 有限的資源內(288),可以去完成那幾個 stories(D應該做不完,E跟F就不要想了...除非運氣很好。
在 PO 很清楚知道資源有限的狀況下,他就必需去做抉擇。如果現在只有 1 個 sprint 的時間,你沒辦法 A~F 全做,你要考慮清楚最重要、對客戶最有價值的到底是那幾個!全做當然最好,但資源攤在你眼前,很清楚的告訴你:不可能!
所以我覺得透明性的帶來的好處,除了明確了解需求外,也可以讓 PO 理解資源有限,他必需非常認真的選擇他要的功能!
註1:我們認為每位 developer 一天當中完全專注在工作上的時間是六小時左右,扣掉上廁所、休息、上網,工作轉換的overhead等)。
註2:一個 sprint 為二週,扣掉第一天的 Planning Meeting 及最後一天的 Review & Retrospective meeting,剩下8天。
註3:估算的過程重點在”溝通”,實際上的時間可能還是會不一樣。
敏捷當中的 透明性 帶來什麼好處?
早在我加入目前公司的第二年,大概是 2004 年左右,我就拿到一本同事給我的 Extreme Programming 的書籍。在書上第一次看到 TDD,看書上寫的,TDD 看來是相當美好的。所以當年曾經想要進行嘗試,不過當然最後是失敗了。失敗的原因是 - "那有時間這麼搞,快寫 code 吧"
四年前,有一段時間致力於將內部使用的一個模組改寫成 C++,那時候還是想要再嘗試一遍 TDD,不過寫了兩天,就又放棄了。這次並不是因為時間壓力,而是寫起來很彆扭,又急著想要看到結果,所以寫著寫著,TD 就不見了,只剩下最後那個 D。這個專案後來我還是加了不少測試,但是因為不是邊寫邊開發,有些測試變得相當困難,所以測試 coverage 也不高。
這次之後,我幾乎對 TDD 已經死心,半年前看到 Uncle Bob 為 Kenbeck 的實作模式一書所寫的序,對於書中 TDD 的描述,讓我又再次讚嘆不已,但我認為那是大師才做得到的境界,凡人不可能達到。所以看過,也就放掉了。
前幾天,利用坐高鐵下南部的來回時間,將 Uncle Bob 的 Clean coder 翻了一遍,我終於知道,為什麼我 TDD 總做不來。其實就像一個連轎車都不會開的人,想要開飛機就會很困難,因為你不夠熟練。在 Clean coder 的第六章,Uncle Bob 寫到他每天都要花一些時間進行程式寫作的練習。而且將這種練習比喻成彈鋼琴或者運動選手的每日基礎練習,頓時讓我豁然開朗:TDD 是一個寫程式的方式,如果沒有經過練習,就開始應用在中大型的專案,當然很難會成功。就像一個鋼琴選手,每天不練習,就直接上台參加蕭邦鋼琴大賽,結果應該不難想像。
所以從現在開始,想了解新的語言或寫程式的方式,不要只是念書,該練習。如果你只想要練習 TDD,又不想花一大把時間去架設環境,安裝軟體,可以試試 cyber dojo。只要有 web browser 就可以做練習,還支援一大票 language 喔。
TDD 恩仇錄
「不要試圖覆蓋所有使用案例。Spec.不是用來替代組合回歸測試的。」 - Spec by Example 中文版 p.151
但我們明明覺得現在用 Robot Framework 實現自動化 UAT 來替代組合回歸測試是很正確的做法,為什麼作者覺得這樣不對?
我認為是因為作者在討論的是 Spec by Example ,或著說想用 ATDD 的方式來開發軟體,而一旦變成回歸測試,就失去了最初的目標了。
但是..
我認為是因為作者在討論的是 Spec by Example ,或著說想用 ATDD 的方式來開發軟體,而一旦變成回歸測試,就失去了最初的目標了。
但是..
耶,這是怎樣,其實要拿來做 regression tests 也是可以的哦!?
我覺得其實是可以的,而且我們現在也在這樣做,但是用 UAT 做回歸測試有一些副作用,例如需要很久的時間,因此解決這些副作用才是最重要的!現在雲端主機要租用非常方便,我認為可以用平行處理,把 UAT 分散在很多台機器去跑,這樣就可以讓測試時間縮短。
應不應該用 UAT 做回歸測試?
前言
最近在tune ext4 的格式化速度,發現ext4 有 flex_bg 這個 feature 可以用,就開起來測看看。關於 flex_bg 的詳細內容可以參考 ref
概略來說就是將幾個block groups 組成一個 flex_bg
它會將每個 flex_bg 的 group 0 之後的 groups 的 DB bitmap, inode bitmap, inode table
存放到 group 0 統一管理(放不下的時候會放到下一個group)
存放的時候每個 DB bitmap, inode bitmap 都會佔 1 個 block, inode table 會佔 8 個 block
所以DB bitmap, inode bitmap, inode table 之間在開始存放的時候都隔 $GRUUP_SIZE 個blocks (-G $GRUUP_SIZE帶入)
測試
flex_bg 預設的 group size 是 16,使用2的冪次遞增group size來測試的時候一開始一切看
起來都很美好,format 時間隨著group size 增加而下降。一直到 group size 指定為
8192的時候忽然發現 format time 暴增...
這真的蠻奇怪的...網路上也找不到相關的討論。於是只好自己去看 e2fsprogs 裡面關於mke2fs這一段的實作在幹嘛。
結論
首先 在 block size 4KB 下 ext4 管理用的 bitmap 最多 可以 mapping 32K個 block (bitmap佔1個block 32k bits)
所以 blocks per group 最多就是 32768 (可以調小 調小有可能會造成mke2fs 在 -G4096 的時候變慢 後面說明)
以 8K 的 group size 配置的時候,這些metadata(DB bitmap, inode bitmap, inode table)的初始擺放位置
分別是第 1025, 9217, 17409 個 block (中間都間隔 8192 個 block)
這在放置到第 1919 個 group 的 metadata 的時候會發生一個現象
可以看到在配置第 1918 個 group 的 inode table 時即將超出 group 0 的邊界了(32768 blocks per group)
所以在配置地 1919 個 group 的 inode table 時 mke2fs 的實作方法會回頭找 free block 來
存放。這時候找到的是第 2945 個 block(當然如果 group 0 中都沒有 free block 放
metadata 的時候 會開始往 group 1 放不過 mke2fs 的策略看來是 group 0 還有空間就先用
group 0 ... 就算最後一定不夠用如果它是接著往 group 2 放 inode table 的話 就不會有現在的問題了...)
這就造成原本理想上算好間隔分散存放的 metadata 開始交錯配置了。在找free block的時候
原本都是馬上返回下一個free block 的 function call (flexbg_offset)在開始交錯配置以後
都要再做一些 search 才能找到下一個可以用的blockprofile
整個 mke2fs 過程花最多時間也是在 flexbg_offset 這個 function call
(對 1TB 硬碟format 整個 format 過程 總共花 126 秒 其中有121 秒花在 flexbg_offset 的處理)
所以在 GROUP_SIZE 是 8192 的時候會因為這些 metadata 在 group 0 裡面配置的時候容易發生交錯放置的情況
以至於 mke2fs 在搜尋下一個可以放metadata 的 block 的時候 比起其他的 GROUP_SIZE 花比較多的 overhead。
另外
如果在 -G 8192 的時候加上 -b 8192 的 option (指定 block size 成 8K)->
則 -G 8192 就會變得跟 -G 4096 速度差不多。這是因為 block size 變 8K -> blocks per group 變 64K
(其實有少一些零頭 實際上是 65528個,bitmap 可以 mapping 的 block 數 變 64K )
則 inode table 配置的時候 不會很快遇到 group block 邊界的問題 就比較不會發生和 DB bitmap 或 inode bitmap 交錯配置的情況
mkfs.ext4 -O flex_bg -G8192 is very slow
在做自動化 UAT 時,最常做的事便是拿 HANDLE,這在以前傳統的 GUI Framework 只要用 Spy++ 或其它開發工具都很容易做到。
但現在 QML 不一樣了,QML 裡頭已經沒有所謂的 window handle,比較接近的是 objectName,你可以用 objectName 來對該元件進行操作(get property, call method…),問題是要怎麼樣拿到 objectName 呢?最直覺的作法就是直接看 QML source code,有錢一點的可能是用 Squish 之類的工具去做。但是直接看 QML source code 其實是比較花時間的,而 Squish 則是價格比較高,因此我實作了一個 JavaScript Library 可以讓你在 UI 上顯示每個 QML component 的 objectName。
例如你的 GUI Application 原本看起來是這樣:
在呼叫了 LabelQML.addObjectNameFlag(this); 之後,它便會在所有有 objectName 的元件上顯示一個小紅點:
當你滑鼠移至小紅點上方時,便會在畫面中央顯示它的 objectName:
完整程式碼及範例:
https://github.com/diro/LabelQMLComponent
其實作法很簡單,基本上就是 recursive 去列舉所有的 component,並檢查是否有 objectName,若有則建立一個 dynamic QML component(小紅點),當滑鼠移至小紅點時,便會在預先建立的 displayBoard 元件中顯示 objectName。
但現在 QML 不一樣了,QML 裡頭已經沒有所謂的 window handle,比較接近的是 objectName,你可以用 objectName 來對該元件進行操作(get property, call method…),問題是要怎麼樣拿到 objectName 呢?最直覺的作法就是直接看 QML source code,有錢一點的可能是用 Squish 之類的工具去做。但是直接看 QML source code 其實是比較花時間的,而 Squish 則是價格比較高,因此我實作了一個 JavaScript Library 可以讓你在 UI 上顯示每個 QML component 的 objectName。
例如你的 GUI Application 原本看起來是這樣:
在呼叫了 LabelQML.addObjectNameFlag(this); 之後,它便會在所有有 objectName 的元件上顯示一個小紅點:
當你滑鼠移至小紅點上方時,便會在畫面中央顯示它的 objectName:
完整程式碼及範例:
https://github.com/diro/LabelQMLComponent
其實作法很簡單,基本上就是 recursive 去列舉所有的 component,並檢查是否有 objectName,若有則建立一個 dynamic QML component(小紅點),當滑鼠移至小紅點時,便會在預先建立的 displayBoard 元件中顯示 objectName。
How to get and display QML objectName in application
一直都是使用 Robot Framework 來進行自動化的 UAT,但因為完整的 UAT 還包括了手動測試的部份,因此在 UAT 中 Test Case 的管理上就比較麻煩一點,可能完整的 test case 是存在 excel 裡頭,再由開發人員手動填入 Robot Framework 的測試結果。
這樣真的太低級了,這不但浪費時間、易出錯,而且很難管理啊,所以必需有一個更好的管理方式。
為了解決這個問題,我們導入了 TestRail。TestRail 是一套相當好用的 Test Case / Test Plan 管理系統,而且 API 相當完整,因此拿來跟 Robot Framework 整合真是再適合不過了。使用的流程為:
- 在 TestRail 中建立Test Case
- 如果該 Test Case 有對應的 Robot Framework 自動化測試 test case,記得在 test case 中的 Tags 欄位加上 CID:n,這個 n 便是在 TestRail 中的 CID(Case ID)
- 當產品要進行完整測試時,便建立一個 Test Run,然後由 CI Server 呼叫 Robot Framework 開始進行自動化測試,接著再自動把測試結果更新到 TestRail 中對應的 Test Run Result 中(passed/failed)。接著團隊便可以知道還有那些 Test Case 未經測試(untested),只要針對這些 test case 去補足手動測試即可。
How to Integrate Robot Framework with TestRail
Clean code 裡頭我想談的第二個讓我豁然開朗的地方就是他談單元測試。到底該不該做單元測試,我想目前已經沒有疑義了,但是究竟單元測試該測到什麼地步呢? 我們直接看 Bob 大叔在書內程式碼的的氣味和啟發一章所提關於測試的九點:
- 不足夠的測試 - 一套測試組應該包含多少個測試呢? 不幸的是,許多程式設計師所使用的標準是 "這看起來似乎是夠多了"。一套測試組應該測試所有可能失敗的情況。只要有任何的條件沒有被測試過,或任何的計算沒有被驗證過,那麼這套程式組就還是不足夠的。我的媽啊! 這真的是嚴苛的標準啊,看起來寫程式與寫單元測試的時間真的應該是 1:1 甚至是 1:2 才對。
- 使用涵蓋率工具 - 涵蓋律工具告訴你,你的測試策略裡有哪些地方並未測試到。他們也讓你容易找到尚未測試構的模組、類別和函式。大部份的 IDE 會提供你視覺上的指示,將已經被測試涵蓋到的程式碼標成綠色的,然後將為涵蓋到的程式碼標是為紅色的。這樣子可以更快速和容易找到那些還沒有被測試道的 if 或 catch 敘述。基本上,這還是再強調覆蓋率必須百分之百啊!
- 不要跳過簡單的測試 - 這些簡單的策是很容易撰寫,而且這些測試文件的的說明價值高於撰寫的成本。好了,一個壞消息一個好消息。壞消息是,還是得有百分百的涵蓋率,好消息是,寫測試有其他的意義。那是不寫測試得花更多成本達成地。
- 被忽略的測試是對模稜兩可的疑問 - 有時候因為程式的需求不夠明確,所以我們無法確定某個行為的細節。我們可以利用被註解掉的測試,或者被標記成 @Ignore 的測試來表達我們對於需求的疑問。至於你該選擇哪種做法,取決於模稜兩可的部分是否想要被編譯。嗯,作者幫我們找了第二個測試可以提供的用途,感覺比較好一點了。
- 測試邊界條件 - 特別注意測試邊界條件,我們會遇到,演算法在一般狀況下運行順利,但在邊界條件時卻判斷錯誤。好,這是一個很好的線索,這可能是所有測試中,除了功能要符合預期的測試外,最重要的測試項,所以可以把這類測試擺在比較前面實作。吚? 我有說如果時間不夠,其他的來不及就算了嗎? 是你聽錯了吧!
- 在程式錯誤附近進行詳盡的測試 - 程式錯誤往往聚集。當你再函式裡找到否個程式錯誤,明智的做法是對該函式做一個詳盡的測試。你可能會發現程式錯誤其實並不只有一個。這個明顯是再談如果你用到一段沒有單元測試的原始碼的狀況,或者你的系統原來沒有單元測試,後來被 QA 找出問題後的處理方式。所以新寫的程式碼,還是應該按照前面的幾個原則寫單元測試。
- 失敗的模式是某種啟示 - 有時候你可以利用測試程式失敗的模式,來診斷程式的問題在哪裡。這是另一個要使得測試盡可能完整的論點,完整的測試以合理的方式排序時,就能透露出失敗的模式。這段中文的意思看不是很懂,不過我看得懂他說了,"測試盡可能完整"。
- 測試涵蓋率模式可以是一種啟示 - 查看在測試時執行過或未被執行的程式碼,就能對失敗的測試為什麼失敗,提供一些蛛絲馬跡。說了半天,就是說沒有測試過的程式碼,會隱藏出錯的機會,所以涵蓋率要夠廣。
- 測試要夠快速 - 一個龜速的測試是個不會被執行的測試。當行程很趕時,龜速的測試會從測試組裡被移除。所以盡力保持你的測試程式夠快速。這點跟涵蓋率比較不相關,但是提醒我們單元測試的使用情境,一定要保持他快速執行的特點。
所以通過這九點觀察,我們可以得出一個結論,請追求盡可能的單元測試涵蓋率。所以導入單元測試對於過去沒有寫測試的人來說,就是一個雙倍時間的投資。不過我相信他絕對是值得的。
Clean code (2) 再談 Unit test
過年期間翻了這本書,可能是我有年紀了,看這種技術偏硬的書著實有些吃力了,一個過年下來還沒有看完。不過,這本書看了真的會讓你猛點頭。有些過去自己感覺不太對的地方,看了這本書有點豁然開朗的感覺。
比如說,以前看到程式裡頭有一些被註解的程式碼,總會覺得怪怪的。也曾經對成員宣導過不要留註解的程式碼在 source code control 上。但是這樣的程式碼在很多地方都看得到,包括我們使用的 open source 的原始碼當中都有不少。
過去的習慣在改掉程式碼前會先將原來的程式碼註解,一方面當作參考,另一方面也是怕改爆了,可以馬上復原。但我們比較少做的是,在確認修改完成後,並沒有清理戰場,把不必要的垃圾清除。那些被註解的程式碼久了就會變成垃圾。因為沒有人知道打開註解後會不會動,也不會知道該不該打開。
舉一個例子,假設一段程式碼有兩種實作方式,而現況是註解一種,打開另外一種。過了一年半載,如果維護的人換了,這就會變成無頭公案,沒有人知道這兩段怎麼來的,也不會知道接下來怎麼維護。
如果你的想法是,遇到這種狀況,再寫一個註解去說明這兩種實作的差異,那麼你很會就會遇到程式碼與註解同步的問題。比如說現在開啟的是第一個實作,但是某個罕見的場景失敗了,被回報 bug,這時候解 bug 的人看到有第二個實作,他的想法會是 "先修改註解,然後再交換兩種實作的註解狀態" 還是會 "直接先交換註解狀態,試試看,耶 OK 了,然後就把註解的事情忘得一乾二淨" 呢? 我猜後者的可能性居多吧。所以時間一久,維護換了幾手,整個程式碼就會變得很難相信。
另一個例子是過去在 visual source safe 年代,一個檔案的前面會有修改歷史紀錄,而且這些紀錄是系統在你每次 checkin 自動加上去的。所以當你打開檔案時,得先翻個四五頁才看得到真正的程式碼。過去我覺得這怪怪的,所以後來內部因為組織架構改變,程式碼分家後,我馬上就砍了這些註解。到現在我還有點心虛,這樣到底對不對。不過看了 Bob 大叔的話,我馬上就理解,砍掉是對的,保存歷史應該是系統的事,而不是放在檔案裡頭。
另外過去有一段時間,我很執著在自動產生文件這件事,所以找了 doxygen,也一度要求函式開頭要有一堆註解的說明。到後來這個動作也自然被淘汰了。原因無他,因為這種註解只有被創造時與事實相符,之後就變成誤導人的東西了。
最震撼的應該是程式內文內的註解,因為過去的訓練幾乎都在談,該利用註解來輔助你的讀者了解你的程式碼。不過 Bob 大叔很直接地說了:註解無法彌補糟糕的程式碼、用程式表達你的本意。這就是說,盡量不要用到註解,如果需要用註解才能說明,就表示你的程式碼需要被重構,直到他們能夠達意為止。
最後,你是不是看過程式碼有不少的 TODO? 你有沒有計算過這些 TODO 多久會消失? 我想,大部分的 TODO 都是恆久遠地,重來沒有被移除地。即使他們想傳達的東西後來已經沒有必要,或甚至已經被做出來了。但是因為註解的維護不那麼重要,所以通常不會被更新。這就證明了 Bob 大叔的觀點看來是有道理的,所以是不是該去清清程式碼裡頭無用的註解了呢?
Clean Code (1) 註解
自動化UAT的一些體悟
由於自動化UAT對scrum team來說是一個全新的技術, 所以在過程中往往會有很多天馬行空的測試方法, 這篇文章就以我目前運作一段時間以及跟組員們討論如何測試所理出一點心得.
所謂UAT(User acceptance testing, http://en.wikipedia.org/wiki/Acceptance_testing#User_acceptance_testing), 就是對一個solution依照user提出的requirement(或是我們的story中的how to test)驗證solution是否正常.
所以這邊我先點出一個重點, UAT測試的是一個solution, 以我們做監控軟體而言, 測試最大的涵蓋範圍就有, UI, functional plugin, Client/Server framework, recording server….所以他是一個整合性測試, 千萬不要覺得UAT只是拿來測試UI而已.
會有這個體悟是因為有天member在跟我討論如何驗證UI上顯示搜尋警報結果的功能. 這功能包括從server搜尋符合條件的警報, 回傳到client處理並建立model, 最後UI透過grouping的規則呈現這個model的內容.
問題就出在如何產生驗證資料, member認為應該是另外寫一段code從client的model拿到資料並根據grouping的規則, 再去跟UI呈現的內容作比對. 單就這方法而言如果只是測試UI的功能正不正常或許是ok的. 但這有幾個問題:
- 那誰要去驗證你寫出來的驗證程式正不正確.
- 就是我說的這個測試只能針對UI, 而不能是對整個solution做測試, 因為沒有測到client拿到sever資料以及組完model時的正確性.
- 想看看最前線寫test case的人員是DQA(或是PO跟user討論), 我想他們百分之百不會想到要用程式去產生一份驗證資料出來. 對DQA而言, 他們一定會先定義好測試條件跟環境, 然後針對各種條件就先把驗證資料蒐集好了, 這樣測試時才可以拿來跟solution得到的結果比對.
到這邊我要提出另一個重點, 就是測試資料跟測試環境的重要性, 這裡我有個慘痛的經驗, 就是我們的第一個自動化UAT的測試資料是用程式碼自動產生的, 也就是在test case要跑之前就先隨機產生測試資料, 我想就是這個錯誤導致member在驗證test case遭遇很多麻煩. 由於測試資料的不確定性, 導致test case每次測試時的驗證資料都不會是相同的.
測試資料環境的不確定性還會導致一個問題, 當發生test case不過的情況時, 你變成要去查到底是新增的程式碼發生問題, 還是測試資料或環境不合乎這個test case而造成的問題.
針對這問題, 後來我們就開始著手測試環境的建置, 針對每一個test case都應該要有他們測試環境跟驗證資料. 我相信要做到自動化UAT這個測試環境一定要花很多心力去建置. 但總比用程式去自動建置來的好太多了, 光想到當初開發跟maintain那些程式碼就讓我一度懷疑自動化UAT的好處到底在哪裡.
總結一下, 自動化UAT我認為就是把驗證test case的過程做自動化而已, 所以一個test case必須要先定義好以下幾點:
- 測試環境跟測試參數
- 測試步驟
- 驗證的資料
相信只要有以上的資料要做自動化UAT一定不是問題,歡迎大家一起來討論UAT。
自動化UAT的一些體悟

http://www.taaze.tw/sing.html?pid=11100723738
本書獲得《Dr. Dobb’s Journal》肯定,榮獲第21屆Jolt獎。
打造優良的軟體開發團隊,除了要大家有敏捷的觀念外,基礎建設也是非常重要的。前一陣子唸完了 Continuous Delivery 之後, 便開始計畫要把不足的部份補完(如果不知道 Continuous Delivery 有什麼好處,可以先上網查查,或著看看書 XD),在幾位同學的努力之下,花了一個多月把缺的部份補上,包含架設CI Server、夠好用的build script、Robot Framework for QML、Unit Test integration。
- CI
- 我們選定的 CI 是用 TeamCity,其實 Jenkins 也不錯,但是比較起來商業化的軟體在易用性及介面上,還是略勝一籌,加上之前也都是在用 TeamCity,我覺得用 TeamCity 可以更快上手。
- Unit Test
- Unit Test 使用 Google Test,為何捨棄原本的 Boost Testing Framework?一個原因是它在 mock/stub 這一部份還不及 Google Test 完整,因此我們選用了 Google Test。
- UAT (User-Accaptance-Testing)
- UAT 的部份則是使用 Robot Framework,沒有特別原因,只是因為有其它同事正在用,就一起用吧 XD
- Qt/QML Testing Framework,
- GUI Application 的測試,有一點很重要的是要以 HANDLE、object ID 來操作,而避免使用”圖形”的方式來操作,因為你的 GUI 有非常大的機率會調整,只要一改,你的 test case 就要全部重來了。但是 QML Application 裡頭是沒有所謂的 window handle 的,跟傳統的 Windows Application 不大一樣,而目前市面上只有 Squish 有辦法去對 QML Application 做到使用 object ID 來操作。(Ubuntu 的 testing tool 似乎也有,但沒有深入研究)
- 但我們並沒有使用 Squish,而是讓我們的程式原本就具備這樣的”可測性”,也就是說我們自己做了一個 python library,可以讓開發人員透過這個 python library 來操作我們的 QML Application。而 Robot Framework 就是透過這個 python library 來完成 keyword-driven testing。不知道什麼是 keyword-driven testing 的話,可以參考 http://kb.froglogic.com/display/KB/Article+-+Keyword-driven+testing+with+Squish+and+Robot+Framework
就這樣,透過上面四個元件的合作,我們完成了 Continuous Delivery 的目標。這個系統會自動完成下面的工作:
- 有團隊成員 commit code 之後,開始進行 build,然後執行全部的 unit test
- 通過 unit test 之後,自動打包成準備釋出的 installer.exe
- 接下來自動依序發佈到不同平台的 VM 上去進行測試。
- 系統呼叫 sikuli,自動在各個 VM 裡頭執行 installer.exe,並完成軟體的安裝。
- 安裝完畢,會呼叫 Robot Framework 開始進行 UAT (User-Acceptance-Testing)。
- 完畢,產生報告。
有了 Continuous Delivery,現在 RD/UX/PO 都隨時可以下載最新版的”已測試過"軟體來使用,再也不用等人手動去 build 出來了。
Continuous Delivery
很多人問我為什麼要在 Hackathon Taiwan 做無償的講師(當然,還要感謝另一位被我拐來的 Jack Yang),我可以從中獲得什麼好處呢?
坦白說,並沒有,就像我在 Qt@Taiwan 中社團簡介寫的一樣:"非營利"。
那我到底幹嘛吃飽這麼閒呢..
我很單純的,希望可以看到台灣有更好的軟體發展環境,有更多熱血青年可以投入這個領域,用軟體(包括firmware,所以軟硬體結合的產品也在我講的範圍內)帶領台灣走向下一個世代、走出國際。
我個人對 Qt 的 source code 研究還算不少,我覺得在 Lars Knoll 的領導下,Qt 真的是一個非常棒的設計,不論是你要拿來開發 Application,或是想從他的架構中得到啟發,我覺得它都是個很好的典範,而且 QML 讓開發效率與執行效能達到一個完美的結合!因此,我很努力的推廣Qt / QML,希望讓更多人可以認識 Qt,並從中學習到東西(當然,網路上還有很多偉大的 project 值得我們去挖寶)。
剛才在高鐵上不斷有人傳訊來問我今天 Qt/QML 的問題、有人告訴我他要用今天教的東西打造出他想要的作品,我覺得,還是很多熱血青年的啊 XD(所以我剛剛在整理 sample project 整理好給他,然後就又熬夜了....)。
如果說真能得到什麼好處,大概就是有機會接觸到一些不錯的小朋友,可以拐進公司跟我一起工作吧 XD
但是我的初衷是一樣的 - 讓有心的熱血青年,可以在一個好的工作環境中成長!
Diro但是我的初衷是一樣的 - 讓有心的熱血青年,可以在一個好的工作環境中成長!
我為什麼要在 Hackathon Taiwan 做無償的講師
很多書都在強調 code review 的重要性。在我們的軟體部門剛開始成立,並沒有 code review。這個狀況持續了幾年,一直到前年,我才開始認真思考,不做 code review 真的對嗎? 於是我們從那個時候開始逐步推動 code review。
在沒有 code review 的時代,有兩件事情讓我印象深刻。第一件事情已經七、八年了,有一個週末,我們因為程式在下一週初要 release,想當然爾,工作是做不完地。所以我決定週末來加班。那時候我自詡應該當規劃者,所以平常是不寫程式碼地。可是真的來不及了,所以我得跳下來自己幹。沒想到那個週末,當我打開 project 的程式碼一看,我快嚇傻了。一個函式長達一千多行,眼睛東瞄西瞄看到的是許許多多重複的程式碼。這個該怎麼改呢? 到最後就是能頭痛醫頭腳痛醫腳,可想而知,那一個週末沒有什麼好下場了。第二件事發生在三年前左右,我們一個產品內的某一個 service,在屢次的進測,總是領到最多的紅牌。最後在產品被追殺的階段,我們發現那一個 service 無法收斂了。這時候我們開始投人進去那一個 service 進行 code review。不看不會怕,看了之後才知道多可怕。那一個 service 竟然高達一萬四千多行。可怕的是,這一萬四千多行在同一個檔案。是地! 一個 service 就一個檔案。最厲害的地方是,這個 service 破了之前的一千多行記錄,有一個函式高達三千多行。最後那一個 service 只能靠硬測的方式讓他自然地收斂。然後在後面安排 refactor。Refactor 後剩下八九千行,當然也拆了不少的檔案。可以看出來原來的作品中重複的程式碼有多嚴重。我們稱這種大函式叫:"吾碼一以貫之",這種一個函式殺到底的寫作方式,對於寫的人來說當然過癮,不用想太多次該怎麼擺變數,想一次就可以用 "很久"。但是對於看的人來說,實在是痛苦至極,看到第三頁的時候,已經不記得這個變數什麼時後宣告、什麼時候改值。諷刺的是一個函式不能太長這件事,在公司的 coding rule 當中是有提到地,為什麼還會看到這樣的事情出現?問題就在於事情沒有人去看,就容易歪掉。
這種事情就是這樣,開始總是最難地。前年開始推 code review 的時候,第一個遇到的難題就是人力的問題。這其實是一個弔詭的問題,本來寫 code 寫得很用力的團隊,突然要 "插" 一件 "本該做的事",卻變成當時團隊多出來的事情。於是我只能動用手上所有的資源幫忙 review。然後因為遇到大家的時間不容易湊在一起,所以也請成員架上 Reviewboard 來協助分散式 code review。然後先是一個團隊持續地看到 review 活動,然後是另一個團隊的重要修改也開始 review。最讓人驚艷的是去今年初,已經有一個團隊將 Pre-commit review 自動化架好了 (用 Git + Gerrit + Jenkins)。只要 commit code,系統會自動驗證可否 build,然後會寄出 review request 給設定好的人。所以現在信箱一天會收到將近十封的 review request (說實在,加上 build 成功的訊息,一天信箱會被灌入 3, 40 封信,很快就會爆了 XD)。這樣的自動化,最有趣的地方在於讓你的 code 沒有被 review 過就不能夠 commit。因此現在對那個團隊而言,已經不是 optional, 而是 must。所以 review 活動就會被持續執行。而現在也會發現,因為 code review 的工作被拉出來,組長的工作裡頭自然會加入此項 task,所以比較不會像剛開始一樣覺得根本排不進去。
那麼到底這樣強推 review 有沒有什麼效果呢? 說句老實話,如果你期待 code review 可以大量減少 bug 數量,是比較不切實際地。但是他對於防範 non-trivial 的 bug,尤其是架構性問題、易維護問題、後續加新功能的 side effect 的防範,是有比較大的效果。另一個重點是,因為強制 code review,你可以在無形中強迫 member 去看到其他人被要求了哪些東西,下次他自己寫的時候就不容易重複同樣的錯誤。另外,自己寫的東西有人在看的時候就會比較小心一點,這些都是無形的效果。
現在開始有團隊在導入 scrum 了,在該團隊中更增加了 peer 的 code review。不再單純是上對下的 review。我覺得這樣做又有更進一步的效果,那就是懂 code 的人更多了,對於未來 task 的相互支援,也會變得比較有可能性了。這是我們在 code review 開始前享受不到地。
所以 code review 重要嗎? 非常重要。你們開始做了嗎?
Code review 真的重要嗎?
Unit test (單元測試) 在我剛開始加入團隊的時候,有寫過一陣子。那時候使用的是內部人員自己開發的 framework。當時寫 unit test 的方法可以說相當原始,我們沒有特別去研究 unit test 可以怎麼作,stub 或 mock 更是沒有研究過的議題。大家就只是很努力地在每個模組完成後,補完大部分可能的 test cases。但是久而久之,隨著專案越來越大,架構越來越複雜後,寫 unit test 變成一個令人沮喪的活動,至少當時對我而言是這樣。原因是甚麼?
多年後,我在團隊中又剛好擔任到導入 unit test 的第一線角色。現在因為開發經驗比較豐富,對於測試應該怎麼寫,或是寫 unit test 的人應該專注於哪一部分有了一點點的感覺。在我還是個懵懵懂懂的 RD 時,覺得每次寫一個新模組的 unit test,前置大概要花好幾個小時,在寫一些該模組與其他元件「假」互動的部分,例如與資料庫溝通的模組,透過網路傳送訊息的類別,跟串流伺服器傳輸影音的函式庫。因此,寫久了會問自己,到底是在寫單元測試還是在寫假類別的程式。
![]() |
| [圖片來源: www.wowwiki.com] |
從目前的狀況看來,我們只是「有」了 unit test,但是我們還沒有思考覆蓋率,或是維護的問題,持續改善這一部分將是我們要繼續努力的地方。好消息是,我們的 CI 已經整合了 unit test 的部分,這多多少少也會鼓勵大家寫 unit test。只有當 RD 覺得 unit test 很好用時,他們才會願意花時間且認真的撰寫 unit test。我本人也不例外 XD
Unit test 的美麗與哀愁
討論是很重要的一件事,而且有時侯一圖勝萬言,因此如果有白板可以讓大家討論是很重要的一件事。在我們團隊的辦公室中,我們便規劃了三面白板,其中一面是當做sprint wall & burndown chart,另外二面就是 member 可以自由討論的地方,以目前團隊人數(10)來說是還蠻夠用的。這個sprint有一個功能是3位成員一起合作完成的,因此我們在白板上畫出了架構與流程來溝通討論介面該如何設計,溝通清楚後,大家在座位上一抬頭就可以看到當初討論的結論了,真是個好白板 XD。
此外,我們這間還有一台投影機及一台電視,電視最近加上了chromecast,讓團隊成員要分享畫面更加容易,有時侯要分享或討論的東西在電腦上,只要按個鈕就可以顯示在電視上跟大家討論,還挺方便的(不過FPS不高,要做影片的分享較不適合,可能還是要裝個 Apple TV 比較好)。
敏捷團隊很重視團隊成員的互相討論與分享,但如果環境因素讓成員不易分享與討論,就很難讓大家願意去分享與討論了。
你的工作環境會讓成員願意討論嗎?
給團隊成員易於討論的環境
VIVOTEK Digest
這裡是 VIVOTEK 晶睿通訊 的技術部落格,我們在這裡分享與敏捷、Qt/QML及其它軟體相關技術
Trending
-
「不要試圖覆蓋所有使用案例。Spec.不是用來替代組合回歸測試的。」 - Spec by Example 中文版 p.151 但我們明明覺得現在用 Robot Framework 實現自動化 UAT 來替代組合回歸測試是很正確的做法,為什麼作者覺得這樣不對? 我認為...
-
平常 C++ 的開發工具是 Microsoft Visual Studio,然後現在的測試框架是使用 Google Test,以前都是一邊寫,一邊手動執行 test case 來驗證,沒有辦法跟 VS 內建的 Test Explorer 做整合,真的蠻原始的 =.= 最近因為...
-
針對常用的match role, 可利用QHash (or QMap) 多存一份, 來加快 search 效率 我們在設計model通常都會定義一個存unique string value的IdRole當作model item的索引, 且會很頻繁對這個role作ma...
Categories
Programming
19
Culture
11
Testing
6
scrum
6
Agile
5
performance tuning
4
Book
3
Code Quality
2
Qt
2
Event Storming
1
database
1
file system
1
virtual machine
1











