站會(huì)(standup meeting)
站會(huì)中的內(nèi)容是每天工作的開(kāi)始,也是對(duì)昨天工作的回顧。一般會(huì)由團(tuán)隊(duì)的某位成員主持,這位主持人有責(zé)任讓電子系統(tǒng)上的story卡片和看板上的保持一致。站會(huì)上,大家依看板從右至左依次更新自己負(fù)責(zé)story的狀態(tài),如果遇到阻礙,應(yīng)該在站會(huì)上及時(shí)提出,團(tuán)隊(duì)之中的成員如果能提供幫助,應(yīng)該在站會(huì)之后,組織解決方案的討論。
站會(huì)反模式(standup meeting anti-pattern)
- 站會(huì)上由組長(zhǎng)一人發(fā)言、分配任務(wù);
- 一人長(zhǎng)篇闊論占據(jù)大半時(shí)間(合理的發(fā)言時(shí)間為1-2分鐘,總的時(shí)間控制在15分鐘左右);
- 站會(huì)上談?wù)摵蛃tory卡片無(wú)關(guān)的內(nèi)容。
迭代計(jì)劃會(huì)議 (IPM)
迭代計(jì)劃會(huì)議是項(xiàng)目組成員在每一個(gè)迭代開(kāi)始時(shí),聚到一起共同決定這個(gè)迭代的 backlog (代辦事項(xiàng)) 需要交付的故事卡。
這個(gè)會(huì)議的產(chǎn)出物包含:
- 迭代的 backlog 和用戶(hù)故事的驗(yàn)收條件
- 這個(gè)迭代的總體業(yè)務(wù)目標(biāo)
- 故事點(diǎn)數(shù),也即開(kāi)發(fā)測(cè)試人員對(duì)用戶(hù)故事的評(píng)估及承諾
參與者包含:
- Product Owner
- Scrum Master
- 開(kāi)發(fā)團(tuán)隊(duì)(PM, TL, BA, QA, Dev)
- 其他干系人
日程:
日程:
- 團(tuán)隊(duì)確立這個(gè)迭代可以完成的工作量(以點(diǎn)數(shù)計(jì)),一般從歷史迭代中獲取
- 團(tuán)隊(duì)根據(jù)定義的順序討論故事卡,對(duì)于每一張故事卡,需要討論的內(nèi)容包含:
- 衡量故事的相對(duì)大小(以點(diǎn)數(shù)計(jì)),可能會(huì)進(jìn)行故事卡的拆分
- 溝通驗(yàn)收條件
- 根據(jù)已經(jīng)討論出來(lái)故事卡的價(jià)值、時(shí)間和風(fēng)險(xiǎn),PO重新確定故事卡的優(yōu)先級(jí)
- 一旦預(yù)先設(shè)定的工作量到達(dá),就停止本輪迭代會(huì)議
3. 團(tuán)隊(duì)就迭代的業(yè)務(wù)目標(biāo)達(dá)成共識(shí)
用戶(hù)故事 kickoff 會(huì)議
Story kick off,指的是啟動(dòng)一個(gè) Stroy 進(jìn)入開(kāi)發(fā)階段。Story kick off的時(shí)候,通常需要三種角色參與:BA、QA 以及開(kāi)發(fā) Story 的 Dev。
會(huì)議內(nèi)容:
- DEV 自己先完整地過(guò)一遍 Story 的描述;
- DEV 給 BA 和 QA 去講這個(gè) Story 的功能以及 AC;
- 要能夠清晰的講出來(lái),并且三者達(dá)成一致,如果有疑惑,需要當(dāng)場(chǎng)得到解決;
- DEV 開(kāi)始開(kāi)發(fā) Story,并自行將 Story 拆分成多個(gè)子任務(wù)列表,開(kāi)工。
Desk Check / Shoulder Check
Desk Check 發(fā)生在開(kāi)發(fā)人員確信自己已經(jīng)完成了故事卡,需要另外的人(BA、QA、Dev)坐到旁邊一起幫忙驗(yàn)證是否確實(shí)完成。
Desk checklist:
參與者:BA、QA、Dev 或者其他感興趣的人
- 故事卡測(cè)試覆蓋率夠嗎?
- 自己本地測(cè)過(guò)功能么?
- 自己本地跑過(guò)全量的測(cè)試么?
- 所有的 AC 是否都滿(mǎn)足了?
- 做故事卡的過(guò)程中是否遇到了理解上的問(wèn)題?
Defect Prevention Using Agile Techniques
Tech Huddle
開(kāi)發(fā)人員聚到一起就項(xiàng)目中遇到的技術(shù)展開(kāi)討論
前提條件:
會(huì)議之前確定并逐條列出會(huì)議討論的技術(shù)主題
產(chǎn)出物:
- 一個(gè)或多個(gè)行動(dòng)方案,合并到用戶(hù)故事
- 方案的優(yōu)缺點(diǎn)
- 方案的責(zé)任人
迭代回顧會(huì)議(Retrospective)
在每個(gè)迭代結(jié)束時(shí),Scrum Master會(huì)主持該會(huì)議,目的是為了確定哪些改變可以提升下個(gè)迭代的生產(chǎn)效率。在這個(gè)會(huì)議當(dāng)中,每個(gè)人都可以開(kāi)誠(chéng)布公地提出自己的建議,有利于管理者從反饋中獲取團(tuán)隊(duì)的現(xiàn)狀。
最高指導(dǎo)原則 (Prime Directive):
Regardless of what we discover, we understand and truly believe that everyone did the best job he or she could, given what was known at the time, his or her skills and abilities, the resources available, and the situation at hand.
無(wú)論我們發(fā)現(xiàn)了什么,考慮到當(dāng)時(shí)的已知情況、個(gè)人的技術(shù)水平和能力、可用的資源,以及手上的狀況,我們理解并堅(jiān)信:每個(gè)人對(duì)自己的工作都已全力以赴。
內(nèi)容包含:
- 上個(gè)迭代中做的好的有哪些?
- 上個(gè)迭代中做的不好的有哪些?
- 我們可以做些什么改善哪些不好的地方?(改善建議)
參與者包含:
- Product Owner
- Scrum Master
- 開(kāi)發(fā)團(tuán)隊(duì)
- 其他干系人
迭代review會(huì)議 (Showcase)
Showcase 就是開(kāi)發(fā)團(tuán)隊(duì)把開(kāi)發(fā)好的功能給客戶(hù)的 Product Owner(以下簡(jiǎn)稱(chēng)PO)等業(yè)務(wù)相關(guān)人員演示,以獲取他們對(duì)所開(kāi)發(fā)系統(tǒng)的反饋,是敏捷開(kāi)發(fā)流程中的一個(gè)實(shí)踐,一般的頻率是一個(gè)迭代一次,也可以根據(jù)項(xiàng)目具體情況做調(diào)整。
內(nèi)容包含:
敏捷實(shí)踐Showcase的七宗罪
Retro 破冰游戲
用戶(hù)故事(user story)
用戶(hù)故事是指在軟件開(kāi)發(fā)和項(xiàng)目管理中用日常語(yǔ)言或商務(wù)用語(yǔ)寫(xiě)成的句子。這個(gè)句子反映一個(gè)用戶(hù)在其工作職責(zé)的范圍內(nèi)要達(dá)到的某個(gè)目的, 以及此目的所需要的功能。
例子
為了避免遺漏附件延誤工作
作為郵件發(fā)送者
我希望郵件系統(tǒng)能夠在我忘記帶附件的時(shí)候提醒我
格式
為了<某個(gè)目的或價(jià)值>, 作為<某類(lèi)利益相關(guān)者>, 我想要<某個(gè)功能>
作為<某類(lèi)利益相關(guān)者>, 我想要<某個(gè)功能>, 以便<達(dá)到某個(gè)目的或獲得某種價(jià)值>
要點(diǎn)
重點(diǎn)描述商業(yè)的價(jià)值不只是功能:幫助團(tuán)隊(duì)了解需求背后的意圖,利于開(kāi)發(fā)團(tuán)隊(duì)協(xié)同客戶(hù)、業(yè)務(wù)部門(mén)設(shè)計(jì)出更好的解決方案
用業(yè)務(wù)語(yǔ)言描述:利于客戶(hù)、業(yè)務(wù)部門(mén)理解并區(qū)分優(yōu)先級(jí)
利益相關(guān)者不僅包含用戶(hù):系統(tǒng)擁有者的角度也是需求的重要來(lái)源
CCC 組件 (The three 'C's)
- Card (卡片)
- 業(yè)務(wù)價(jià)值 remainder
- 做計(jì)劃和溝通業(yè)務(wù)時(shí)的 token
- Conversation (交流)
- 用于在做計(jì)劃和溝通業(yè)務(wù)時(shí)引發(fā)溝通,制造共同話(huà)題(收斂)
- 需求文檔的生成方式
- Confirmation (確認(rèn))
- 將細(xì)節(jié)以驗(yàn)收測(cè)試的方式檢測(cè)功能的完整性和準(zhǔn)確性
用戶(hù)故事的劃分原則 INVEST
Independent (獨(dú)立性)
故事和故事之間盡量保持獨(dú)立,互相依賴(lài)的故事對(duì)于估算工作量、確定優(yōu)先級(jí)和安排計(jì)劃都帶來(lái)很多不便。通常我們可以通過(guò)組合和拆分的方式減少依賴(lài)性(去除重復(fù))。
獨(dú)立性更多的指的是實(shí)現(xiàn)要完整。前后端拆分通常不是很好的拆分方法
Negotiated (可協(xié)商的)
一個(gè)故事是可以協(xié)商的,故事卡不是合同,它只是包含對(duì)一個(gè)需求的簡(jiǎn)短描述。具體的細(xì)節(jié)在溝通階段產(chǎn)出,以驗(yàn)收測(cè)試的方式。如果帶有太多的細(xì)節(jié),反而限制了和用戶(hù)的溝通。
Valuable (有價(jià)值的)
每個(gè)故事都必須對(duì)客戶(hù)有價(jià)值(無(wú)論是用戶(hù)還是客戶(hù))。一個(gè)讓用戶(hù)故事有價(jià)值的好辦法就是客戶(hù)寫(xiě)下它們。
Estimable (可評(píng)估的)
開(kāi)發(fā)團(tuán)隊(duì)需要去估計(jì)一個(gè)用戶(hù)故事以便確定優(yōu)先級(jí),工作量,安排計(jì)劃。如果難以估計(jì)故事的時(shí)間, 意味著:
- 領(lǐng)域知識(shí)的缺乏: 這種情況下需要更多的溝通
- 技術(shù)實(shí)現(xiàn)的模糊: 這種情況下要做試驗(yàn), 做原型
- 或者故事太大了: 這時(shí)需要把故事切分成小些的
- 還有對(duì)其它團(tuán)隊(duì)的依賴(lài)...
Small (短小的)
一個(gè)好的故事在工作量上應(yīng)該是盡量短小的,至少確保能在一個(gè)迭代或 Sprint 中能夠完成。用戶(hù)故事越大,在估算、計(jì)劃安排等方面的風(fēng)險(xiǎn)就越大。
Testable (可測(cè)試的)
一個(gè)用戶(hù)的故事必須是可被測(cè)試的,以便它是可以完成的。如果一個(gè)故事無(wú)法測(cè)試,那么就無(wú)法知道它何時(shí)可以完成。一個(gè)不可測(cè)試的例子:為了節(jié)省時(shí)間,作為用戶(hù),我希望軟件是易用的。
判斷標(biāo)準(zhǔn) (經(jīng)驗(yàn)準(zhǔn)則)
- 可以寫(xiě)在 Release Notes 里
- 值得講給其它行業(yè)的人聽(tīng)
- 可以寫(xiě)在市場(chǎng)宣傳材料中
驗(yàn)收條件(AC, acceptance criteria)
- 捕獲預(yù)期的行為:一般驗(yàn)收條件都會(huì)在開(kāi)發(fā)之前準(zhǔn)備好,用于捕獲預(yù)期的系統(tǒng)行為,同時(shí)作為故事卡業(yè)務(wù)描述的一部分,定義了故事卡的 DoD(Definition of Done)。
格式
- Given:前置條件,告訴我們?cè)谶M(jìn)行操作之前,需要設(shè)置和完成什么;
- When:觸發(fā)結(jié)果的操作
- Then:操作之后的預(yù)期結(jié)果
AC編寫(xiě)的最佳實(shí)踐模式
1. 可讀的
我們希望業(yè)務(wù)人員審閱和修正驗(yàn)收條件,如果寫(xiě)的內(nèi)容只有開(kāi)發(fā)人員能懂,我們就失去了獲得反饋的機(jī)會(huì)。使用上述書(shū)寫(xiě)格式,可以提高可讀性。
Given: that my mobile phone is switched on
And: I have sufficient signal to make a call
When: I dial a number
Then: I am connected to the person I want to talk to
And: incoming calls are diverted to my voicemail
2. 可測(cè)試的
反模式1 - 模棱兩可的陳述
Given: that I have the search page loaded
When: I perform a search
Then: the search results come back within a reasonable period of time
這里的 reasonable period of time 就是不可測(cè)試的,因?yàn)闆](méi)有人可以決定什么才是 reasonable.
合理的改法是:
Given: that I have the search page loaded
When: I perform a search
Then: the search results come back within 5ms
5ms 之內(nèi),這是一個(gè)標(biāo)量,完全可以衡量。
反模式2 - 非系統(tǒng)輸出
Given: that I am on the home page
And: I am logged in
When: I navigate to account preferences
Then: I can see my account preferences
這里的 I can see my account preferences 是無(wú)法進(jìn)行斷言的,因?yàn)檫@是系統(tǒng)無(wú)關(guān)的,說(shuō)得極端些,假如用戶(hù)閉上眼睛,這個(gè)功能就沒(méi)法通過(guò)驗(yàn)收了。
合理的改法是:
Given: that I am on the home page
And: I am logged in
When: I navigate to account preferences
Then: my account preferences are displayed
這個(gè)時(shí)候,我可以檢查系統(tǒng)展示了我的用戶(hù)頁(yè)面。
反模式3 - when 隱藏到 given
Given: that I am on the homepage
And: I navigate to the search
When: I look at the page
Then: the search options are displayed
基本上,這是團(tuán)隊(duì)編寫(xiě) AC 時(shí)最容易犯的錯(cuò)誤,操作出現(xiàn)在前置條件中,when 反而不是系統(tǒng)行為了。
合理的改法是:
Given: that I am on the homepage
When: I navigate to the search
Then: the search options are displayed
3. 實(shí)現(xiàn)無(wú)關(guān)的
驗(yàn)收條件應(yīng)該是實(shí)現(xiàn)無(wú)關(guān)的,它和用戶(hù)故事一樣,是給業(yè)務(wù)和開(kāi)發(fā)人員提供交流憑證的一種工具,所以它應(yīng)該聚焦于功能,而不是功能的展現(xiàn)形式。
Given: that I am on the home page
And: I am logged in
When: I navigate to advanced search
Then: the advanced search web page must be displayed
And: a text box labelled "Name" is displayed
And: a text box labelled "Description" is displayed
And: a command button named "Search" is displayed
這里已經(jīng)框死了必須要使用 text box 實(shí)現(xiàn)展示功能,而實(shí)際上其背后真正的意圖是通過(guò)屬性字段進(jìn)行搜索,隱藏了業(yè)務(wù)含義的驗(yàn)收條件是不可取的。
合理的改法是:
Given: that I am on the home page
And: I am logged in
When: I navigate to advanced search
Then: the advanced search is displayed
And: an option to search by name is displayed
And: an option search by description is displayed
And: the advanced search is displayed in accordance with the attached wireframe
換句話(huà)說(shuō),驗(yàn)收條件本身不應(yīng)該關(guān)注于展現(xiàn)形式,當(dāng)然,為了便于理解,wireframe 是提供直觀素材的更好的方式。
代碼評(píng)審(code review)
代碼評(píng)審是團(tuán)隊(duì)針對(duì)今天一天提交過(guò)的代碼的評(píng)審會(huì)議。這樣做的目的是去除代碼的壞味道,減少累積的技術(shù)債。
團(tuán)隊(duì)成員使用版本控制工具,輪流展示自己一天的編碼成果并且說(shuō)明代碼的用途。這期間,其他的成員不僅要評(píng)審代碼的邏輯是否正確(滿(mǎn)足驗(yàn)收條件),還必須思考是否有更優(yōu)雅的方式實(shí)現(xiàn)這段功能。大家可以隨意表達(dá)自己對(duì)這些代碼的好惡,代碼所有者也可以據(jù)理力爭(zhēng),所有者一旦被說(shuō)服就必須無(wú)條件地按意見(jiàn)修改。為防止遺忘,可以使用便箋紙記錄下來(lái)。
結(jié)對(duì)編程(pair programming)
通俗地講,結(jié)對(duì)編程就是兩個(gè)人同時(shí)工作在同一個(gè) Story 上,一起討論 Story 的解決方案,并編寫(xiě)代碼實(shí)現(xiàn)功能,一個(gè)人敲鍵盤(pán),一個(gè)人看屏幕,穿插著進(jìn)行。Pair的雙方在快速敲擊鍵盤(pán)的時(shí)候會(huì)伴隨一些交流。能力相當(dāng)?shù)膬扇耍梢砸蝗藢?xiě)測(cè)試,一人寫(xiě)實(shí)現(xiàn)代碼。
測(cè)試驅(qū)動(dòng)開(kāi)發(fā) (TDD)
TDD,即測(cè)試驅(qū)動(dòng)開(kāi)發(fā),強(qiáng)調(diào)的是測(cè)試先行,跟我們先實(shí)現(xiàn)功能代碼后添加測(cè)試的過(guò)程恰恰相反。測(cè)試驅(qū)動(dòng)開(kāi)發(fā)是一種驅(qū)動(dòng)代碼實(shí)現(xiàn)和設(shè)計(jì)的過(guò)程。我們說(shuō)要先有測(cè)試,再去實(shí)現(xiàn);保證實(shí)現(xiàn)功能的前提下,重構(gòu)代碼以達(dá)到較好的設(shè)計(jì)。整個(gè)過(guò)程就好比演繹推理,測(cè)試就是其中的證明步驟,而最終實(shí)現(xiàn)的功能則是證明的結(jié)果。
實(shí)踐方法:
- Given:初始狀態(tài)或前置條件
- When:行為發(fā)生
- Then:斷言結(jié)果
單元測(cè)試和測(cè)試驅(qū)動(dòng)開(kāi)發(fā)
測(cè)試的價(jià)值表現(xiàn)在兩個(gè)方面:
- 防止已有的功能被破壞
- 驅(qū)動(dòng)出功能實(shí)現(xiàn)
從測(cè)試金字塔描述的層級(jí)來(lái)看,單元測(cè)試是基座。這類(lèi)測(cè)試數(shù)量應(yīng)該是非常多的,而且還有一個(gè)顯著特征——運(yùn)行速度特別快。一般違反快速這個(gè)標(biāo)準(zhǔn)的,基本上可以肯定這不是單元測(cè)試。
基于以往帶團(tuán)隊(duì)的經(jīng)驗(yàn),我給出一些寫(xiě)單元測(cè)試時(shí)常犯的錯(cuò)誤和好的實(shí)踐:
常犯的錯(cuò)誤
- 覆蓋率100%,卻沒(méi)有任何斷言
- 使用Java反射setAccessible(true)訪(fǎng)問(wèn)私有屬性
- 依賴(lài)某些地方產(chǎn)生的數(shù)據(jù)才能運(yùn)行
- 單元測(cè)試運(yùn)行速度超級(jí)慢
- 開(kāi)始測(cè)試之前,準(zhǔn)備巨量的數(shù)據(jù)
- 測(cè)試之間必須以特定順序運(yùn)行
- 上帝類(lèi)(God Object)導(dǎo)致數(shù)以千行的測(cè)試代碼
好的實(shí)踐
- 絕對(duì)不測(cè)試私有的方法
- 當(dāng)方法沒(méi)有返回值,可以通過(guò)測(cè)試異常或者其委托對(duì)象的方式
1. @Test(expected = RuntimeException.class)
2. Mockito.verify(mockObj).doSomething(args…)
3. 靜態(tài)方法調(diào)用是強(qiáng)耦合的信號(hào)。需要重構(gòu)待測(cè)類(lèi)依賴(lài)新接口,新接口定義的方法由子類(lèi)實(shí)現(xiàn),實(shí)現(xiàn)方法中包裝這些靜態(tài)方法。
什么時(shí)候?qū)憜卧獪y(cè)試?
- 新的需求,首先寫(xiě)單元測(cè)試(保證功能實(shí)現(xiàn))
- 已經(jīng)存在的代碼,補(bǔ)充單元測(cè)試 (保證功能不被破壞)
- 發(fā)現(xiàn)Bug,首先加測(cè)試(保證缺陷不再出現(xiàn))
自動(dòng)化測(cè)試(automation testing)
測(cè)試金字塔和 DOT(Depth of Test)
持續(xù)集成/持續(xù)交付(CI/CD)
再次強(qiáng)調(diào)一下,持續(xù)集成是一項(xiàng)團(tuán)隊(duì)務(wù)必遵守的實(shí)踐。
開(kāi)發(fā)過(guò)程中,我們鼓勵(lì)小步提交的代碼控制方式。即當(dāng)開(kāi)發(fā)人員對(duì)story的部分功能編碼完成之后,如果確認(rèn)可以提交到代碼倉(cāng)庫(kù)(如果是分布式的Git,可以考慮提交到本地倉(cāng)庫(kù)),應(yīng)該盡早提交。開(kāi)發(fā)人員的每日工作可以遵循7步提交法,如下:
- 更新代碼;
- 本地編碼;
- 本地構(gòu)建(使用
mvn test
); - 再次更新代碼;
- 本地構(gòu)建;
- 提交到代碼倉(cāng)庫(kù);
- CI上構(gòu)建。
既然是實(shí)踐(規(guī)矩),就有幾項(xiàng)有效的紀(jì)律需要遵守:
- CI紅的時(shí)候不能提交代碼
- CI紅著不能過(guò)夜(不能等到第二天才去修復(fù))
- 任何人提交后CI失敗,無(wú)論原因,都有責(zé)任跟進(jìn)修復(fù)
- 在提交代碼前運(yùn)行所有測(cè)試
- 不注釋掉或刪除失敗的測(cè)試
這也就意味著開(kāi)發(fā)人員需要密切關(guān)注CI的狀態(tài)。CI持續(xù)反饋著軟件的可工作狀態(tài),所以團(tuán)隊(duì)?wèi)?yīng)該把CI的健康狀況列為項(xiàng)目的最高優(yōu)先級(jí),甚至高于開(kāi)發(fā)新特性。
于2017年09月19日