我們仍然以這張圖作為開頭,之前已經(jīng)講了,Project創(chuàng)建、問題相關、字段相關、界面相關的內容。接下來就是最重要也是最復雜的工作流相關的內容。
以前的文章中曾經(jīng)介紹過工作流的思路,主要是2點:
- 子任務驅動主任務狀態(tài)流轉。
- 字段和流程簡化以及自動化流轉。
工作流概述
工作流服務的主體是問題(Issue),也就是Jira當中的核心載體。工作流中有兩個核心概念:轉換(Transition)和狀態(tài)(State)。
我們用最簡單的一個工作流來解釋一下。
在上圖中,TO DO、IN PROGRESS、 DONE表示三個狀態(tài),注意三種顏色也代表了其中的含義,藍色代表未開始,黃色進行中,綠色完成。
當你需要在流程中添加一種新狀態(tài)時,會需要選擇三種顏色之一。
連接兩個狀態(tài)的線,就是轉換,意義就是通過這個行為,將問題從狀態(tài)1變?yōu)闋顟B(tài)2。
狀態(tài)是一個結果,是由通過執(zhí)行轉換得到的結果,所以我們可以在轉換執(zhí)行時定義大量的操作,下圖就是工作流在選中轉換時可操作的內容。
圖中我選擇了 “轉至進行中” 這個轉換,右側出現(xiàn)了五個選擇,我們依次解釋一下:
- 屬性
- 觸發(fā)器
- 條件
- 驗證器
- 后處理功能
屬性
看一個例子吧
這里的用法就是標題和提交按鈕的國際化的鍵值
觸發(fā)器
可以關聯(lián)到Crucilbe和Fisheye對代碼分支和CodeReview自動轉換執(zhí)行。
條件
只有條件被滿足,轉換才可以被執(zhí)行
這里包含Jira自帶和插件增加的一些條件
驗證器
類似表單提交時的驗證腳本,可以進行填寫有效性的確認
后處理功能
轉換成功執(zhí)行后,自定義一些操作。
我們的自動化的流程推進都是需要后處理功能介入。
給大家看一個子任務的后處理功能觸發(fā)自動工作流的例子:
工作流類型
自定義工作流需要從我們實際的管理需求出發(fā),每個團隊的管理方式方法不同,自然問題類型和工作流也不相同。我在這里提出研發(fā)管理實際過程中整理出了四種工作流。
- 主任務工作流
- 子任務工作流
- Bug工作流
- 默認工作流
這樣四種工作流,基本能夠覆蓋到研發(fā)管理工作中的相關流程和角色。
主任務
主任務概念包含故事(Story)和任務(Task),故事一般是由產品發(fā)起對應平常概念中的需求(主要是功能性需求,當然也可能包含非功能性需求,但是一般都會能夠讓用戶感知到),任務一般是由研發(fā)推動的發(fā)起的工作(例如代碼重構、服務拆分、性能優(yōu)化等,用戶可能無法明顯感知的內容)。
主任務就是一個研發(fā)團隊的核心工作內容的概括,所以工作流是需要包含研發(fā)團隊內的所有角色,產品、設計、研發(fā)、測試、運維等。一般工作任務也是順序從各個角色流轉的,所以可以將工作流劃分成這些角色,每個角色都設置待辦、進行中、完成這三種狀態(tài)。其中完成可以設置為下一個環(huán)節(jié)的待辦,這兩個狀態(tài)可以考慮合并為同一個。
子任務
還記得我們的研發(fā)管理原則之一就是簡化操作,實際的任務執(zhí)行者不需要了解整體復雜的設計與邏輯,通過簡單的操作就能夠驅動整體流程前進。這就是要依賴子任務這個概念。
在我們的研發(fā)團隊中,所有的人員具體的可執(zhí)行項都只能以一種類型記錄,就是子任務。子任務的字段精簡,只包含必須的內容。流程簡單,只需要包含待辦、進行中、完成。由于子任務新建后默認就是待辦,所以正常的操作只會包含2個行為,進行中和完成。人員事先用分組進行標記,通過簡單的進行中和完成的狀態(tài)的切換,就能夠推動各自角色的主流程狀態(tài)變化。
Bug
Bug是研發(fā)質量管理的重要形式,Bug的提出方一般是QA,接收方大部分是研發(fā),也可能是產品。在Bug的工作流過程中,流程其實還是比較清晰的,就是圍繞Bug的解決和驗證,在提出、接收方之間流轉。我們要優(yōu)化的點在于,能夠實現(xiàn)流程的人員自動轉換,無需手動指派。
默認
就像我們寫代碼,分支循環(huán)時都會需要一個默認選擇,這個默認選擇就只要最簡單的待辦、進行中、完成即可,甚至只需要待辦和完成,大家可以根據(jù)實際情況來確認。
工作流設計
工作流的設計中有一個重要的前提就是用戶組的劃分,用于對角色的區(qū)分。
接下來會對上面的四種工作流進行詳細的說明,涉及到一些設置或者腳本的地方也會盡量貼出明確的圖例或者代碼。
主任務
主任務的工作流覆蓋到所有角色,所以是最多狀態(tài)的一個,先來看圖。
這個流程中有11個狀態(tài),一開始是默認的待辦狀態(tài)(TO DO),接下來大家其實能看出,根據(jù)角色分成了幾個區(qū)塊,最右側是產品,接下來向左依次是UI設計、研發(fā)、測試,最終是完成。
這里有幾個設計點和大家一起討論一下:
- TODO狀態(tài)可以轉換到任何一個角色的進行中狀態(tài)。
由于不同Story或者Task的情況不同,實際參與的角色是有差別的,如果我們流程限制所有的角色流程必須具備則可能不會那么靈活。所以每個角色的進行中狀態(tài)都是可以直接從TODO轉換過來的,但是完成狀態(tài)只能從進行中轉換而來。 - 所有轉換的都以統(tǒng)一的格式來命名。
可以看到所有的轉換主要分為兩種前綴:轉至和完成,分別對應著從待辦到進行中,和從進行中到完成。這樣做的原因,首先命名的時候有統(tǒng)一的模式比較簡單不用想很多名字,由于在同一個流程圖中,同一個狀態(tài)可能有多個狀態(tài)可以到達,為了后續(xù)自動化工作流的便利我們這么設計是有幫助的(這句話在稍后的子任務的工作流當中就能很好的解釋了)。 - 不同階段的前置節(jié)點不同
產品是最初的階段,一開始一定是產品進行梳理需求并且整理原型和設計方案。
設計的前置輸入一般都是產品,但是也可以獨立發(fā)起一些需求(例如UI設計改版重構等)
研發(fā)的階段,前置節(jié)點可能是產品也可能是UI,因為正常流程是產品設計完原型給到設計,設計做完UI一起交給研發(fā)進行開發(fā)。當然也有可能產品直接就交付給研發(fā)開發(fā)無需另外設計。所以設計和產品都可以作為研發(fā)的輸入。
測試的輸入一定是研發(fā),因為有研發(fā)進行了系統(tǒng)的變更,才需要測試。
最終完成的節(jié)點,大部分情況下都應該是測試確認保證后發(fā)布上線的結果,少量的情況測試可能無法驗證則研發(fā)確認后就直接發(fā)布。
后處理功能
主任務的狀態(tài)很多,但是特殊的配置其實很少,我在這里只有一個特殊的后處理功能設計,場景如下:
我們有研發(fā)中心和客戶支持中心,客戶支持中心可能有一些外部客戶的Bug或者建議會轉為內部的研發(fā)任務。我們會使用“工單關聯(lián)”將研發(fā)中心的Story與支持中心的工單關聯(lián),當研發(fā)內部的Story執(zhí)行“轉至完成”后,變更關聯(lián)工單的狀態(tài)。
這個功能需要安裝一個插件JMWE,提供了 Transition linked issues 這樣的方法。
這里的配置比較清晰
第一行選擇的是要觸發(fā)關聯(lián)問題的什么轉換(可以通過名稱或者ID),下面選擇的是鏈接的類型。
下面Transition screen的部分,我們將“經(jīng)辦人” 修改為 “報告人”,意思就是工單已經(jīng)發(fā)布上線,誰報告的這個問題已經(jīng)可以做后續(xù)的跟進了。
最后會在對應的工單下做一個評論:內部研發(fā)流程已經(jīng)完成并且發(fā)布。
主任務總結
主任務本身是管理方法的體現(xiàn),為了將流程中的各個角色串聯(lián)起來,并且能夠站在較高的視角跟進相關任務推動流程迅速銜接。
所以主任務正常是不會有人去維護的,也就是不會有人主動的管理主任務的狀態(tài),大家都只是看。
子任務
如果說主任務是復雜而簡單(狀態(tài)多而復雜,但是背后的邏輯簡單),子任務就是簡單而復雜。這句話怎么理解,我們先來看子任務的工作流。
看這個圖基本上就明白,我完全沒有調整過這個最簡單的工作流,而且這個工作流只有三個狀態(tài),排除掉初始的待辦狀態(tài),正常只有兩個狀態(tài)可以切換。所以子任務是簡單的。
接下來我們看復雜的意思
從待辦到處理中這個轉換,我們一共有15個后處理功能,除去系統(tǒng)自帶,我們也有9個自定義的后處理功能。
同樣,我們一起來討論一下其中的一些設計點:
- 子任務是主任務的組成部分,所以主任務的人員應該就是子任務人員的集合。
我們的設計,在新建或者開始子任務時,都會將報告人和經(jīng)辦人加入到父任務的責任人字段中,表名整個任務的相關人員有哪些,更快速的溝通。
后處理功能
子任務主要的轉換是兩個,從待辦到處理中,以及從處理中到完成。我們的所有后處理功能都在這兩個轉換上。
待辦到處理中
待辦到進行中一共9條自定義規(guī)則我們一起來看:
日期規(guī)則
1的規(guī)則是使用了設置字段值這個功能,意思就是要在點擊開始的時候,將任務的開始日期置位當前時間。
目的是希望保持子任務的開始時間為實際的時間,不需要操作人手動的調整時間。
解決結果
2的規(guī)則也是使用了設置字段值,是將子任務的解決結果設置到父任務。
目的是希望將子任務的結果能夠在父任務體現(xiàn),從而讓管理著了解是否存在特殊的解決結果。
責任人
3 4 5的規(guī)則也是使用了設置字段值,都是對責任人字段的維護。
目的是希望將子任務或者主任務的責任人字段添加當前涉及到的人員,主任務的責任人字段是為了維護所有參與者快速確認人員范圍。子任務則更多是為了明確責任。
主任務流程推進
12-15的規(guī)則使用的是Transition parent issue (JMWE add-on),這是插件提供的。是觸發(fā)主任務流程流轉。
目的是通過子任務的流轉推動主任務流轉。這里會比較復雜,我們來看一下具體實現(xiàn),下圖
這里我們分為4個部分:
- 設置觸發(fā)主任務流轉
- 設置字段值
- 添加評論
- 設置執(zhí)行條件
設置觸發(fā)主任務流轉:我們要講到命名的原則在這里的作用了。這里觸發(fā)主任務流程的前提,還是要求當前主任務的狀態(tài)是在目標流轉的起始點的。也就是說如果現(xiàn)在的工作流是 A→B→C,如果我們期望執(zhí)行A→B,那當前主任務的狀態(tài)必須在A才能順利到達B,否則不會產生效果。我們的命名規(guī)則里,把所有轉向某個狀態(tài)的流轉都命名為“轉至xxx”,這樣一個方法就能夠觸發(fā)多種起始狀態(tài)下的流轉了。
設置字段值:這個已經(jīng)是比較常規(guī)的設計了,這個流程中沒有需要設置的內容
添加評論:所有的自動化流程執(zhí)行,都會自動添加一條評論到主任務下,記錄由誰推進到什么狀態(tài)。用于后期的記錄跟蹤
設置執(zhí)行條件:這個實際上是最核心的,后處理功能會按照順序全部執(zhí)行,應該執(zhí)行哪個才能夠順利的推進主任務流程流轉,只能通過執(zhí)行條件來判斷。這里面主要還是通過當前用戶的用戶組來判斷角色,從而決定推進主任務的什么流轉。下面是從產品、設計、研發(fā)、測試四個角色的子任務開始腳本:
- 產品經(jīng)理角色
(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-product-pm") && issue.getAsString("任務類型") == "")
這里是判斷當前用戶是在系統(tǒng)用戶組 org-pd-product-pm (產品經(jīng)理) 中的話,并且任務類型沒有特別設置。
用戶分組大家好理解,任務類型是什么意思呢?
我們發(fā)現(xiàn)因為所有人的工作都要落地成為子任務,有些子任務實際上并沒有推進流程狀態(tài)變化。比如產品經(jīng)理的需求預研、可行性分析等,實際上還沒有推進需求落單,暫時不應該將主任務推進到產品設計進行中這樣的狀態(tài)。這種情況可以通過補全主任務流程狀態(tài)來彌補,我們是通過另外一種方式,就是設置特殊子任務類型來避免推進流程到錯誤的狀態(tài)。所以我們形成了內部約定,一旦子任務的任務類型設置了值,就不會推進流程流轉。
- UI設計角色
(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-product-ui") && issue.getAsString("任務類型") == "")
- 研發(fā)角色
(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-coder") && issue.getAsString("任務類型") == "")
- 測試角色
(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-qa") && issue.getAsString("任務類型") == "")
處理中到完成
這里的自定義規(guī)則有5條:
主任務流程推進
6-10 的規(guī)則使用的是Transition parent issue (JMWE add-on),不復述上面的內容了
這里主要只有后處理功能,我們就拆解一下說明
- 產品完成
這部分邏輯首先判斷當前操作用戶是否是產品角色,如果不是直接忽略。如果是的話,會循環(huán)所有的父任務下的子任務,并且判斷是否是歸屬產品角色的任務。如果是產品類型的任務,判定是否已經(jīng)完成。只有除了當前任務以外,所有其他的產品任務都完成,才將主任務推進到對應角色的完成狀態(tài)。
if(!(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-product-pm"))){
return false;
}
boolean result = true;
for(int i=0; i<issue.parentObject.subTaskObjects.size;i++){
if(issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-product-pm")){
if(issue.parentObject.subTaskObjects[i].key != issue.key && issue.parentObject.subTaskObjects[i].getAsString("狀態(tài)") != "Done"){
return false;
}
}
}
return result;
- UI設計完成
if(!(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-product-ui"))){
return false;
}
boolean result = true;
for(int i=0; i<issue.parentObject.subTaskObjects.size;i++){
if(issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-product-ui")){
if(issue.parentObject.subTaskObjects[i].key != issue.key && issue.parentObject.subTaskObjects[i].getAsString("狀態(tài)") != "Done"){
return false;
}
}
}
return result;
- 研發(fā)完成(有測試)
研發(fā)的判斷條件會稍微特殊一些,這里會有一個分析研發(fā)完成之后的轉向有兩個分支,是否需要測試。
邏輯實際上是很簡單,就是循環(huán)所有的子任務,判斷是否存在用戶組為測試的子任務,如果有就轉向“研發(fā)完成待測”,如果沒有就轉向“研發(fā)完成無需測試”
if(!(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-coder"))){
return false;
}
boolean result = true;
boolean hasQA = false;
for(int i=0; i<issue.parentObject.subTaskObjects.size;i++){
if(issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-coder")){
if(issue.parentObject.subTaskObjects[i].key != issue.key && issue.parentObject.subTaskObjects[i].getAsString("狀態(tài)") != "Done"){
return false;
}
}
if(!hasQA && (issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-qa"))){
hasQA = true;
}
}
if(!hasQA){
result = false;
}
return result;
- 研發(fā)完成(無測試)
if(!(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-coder"))){
return false;
}
boolean result = true;
boolean hasQA = false;
for(int i=0; i<issue.parentObject.subTaskObjects.size;i++){
if(issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-coder")){
if(issue.parentObject.subTaskObjects[i].key != issue.key && issue.parentObject.subTaskObjects[i].getAsString("狀態(tài)") != "Done"){
return false;
}
}
if(!hasQA && (issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-qa"))){
hasQA = true;
}
}
if(hasQA){
result = false;
}
return result;
- 測試完成
if(!(currentUser in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-qa"))){
return false;
}
boolean result = true;
for(int i=0; i<issue.parentObject.subTaskObjects.size;i++){
if(issue.parentObject.subTaskObjects[i].assignee in ComponentAccessor.getGroupManager().getUsersInGroup("org-pd-qa")){
if(issue.parentObject.subTaskObjects[i].key != issue.key && issue.parentObject.subTaskObjects[i].getAsString("狀態(tài)") != "Done"){
return false;
}
}
}
return result;
子任務總結
子任務的核心,就是簡單而復雜。簡單的流程才能更好的在團隊內推行,復雜的后臺邏輯才能滿足管理層的管理需求。
Bug
Bug是研發(fā)測試過程中的質量缺陷,流程不會復雜,主要是自動化的流轉和管控要求。
狀態(tài)只有四個,和子任務相比,實際上只多了一個狀態(tài)“已解決”。默認Bug是由測試提出,新建之后狀態(tài)是“待辦”,研發(fā)開始處理轉換為“進行中”,研發(fā)完成后轉換為“已解決”,測試會跟進“已解決”的Bug,如果確認解決則轉換為“完成”,如果未解決則轉換為“進行中”。
后處理功能
關于Bug,最重要的后處理功能實際上在于“進行中”和“已解決”直接來回轉換,所以我們來看一下 “解決問題”和“未解決”兩個轉換的后處理。
解決問題
這里需要關注的主要還是責任人的處理。只有在解決結果是“Done”(其他一般是無法復現(xiàn)、網(wǎng)絡環(huán)境異常、需求設計如此等)的時候,才將當前操作人作為責任人。這個是為了確認研發(fā)的責任歸屬,只有研發(fā)認可并且實際有處理的Bug責任才需要歸屬于研發(fā)。
經(jīng)辦人的會自動分配給報告人,這樣避免研發(fā)手動指定經(jīng)辦人。
未解決
未解決的轉換意義在于說,研發(fā)提交給測試的Bug實際上并未修復,需要退回給研發(fā)。這里是需要累加“退回次數(shù)”這個自定義字段,
<%
int i = issue.get("退回次數(shù)") >= 0 ?issue.get("退回次數(shù)"):0;
i=i+1;
println(i)
%>
經(jīng)辦人也是需要將當前人員直接指派給之前流轉過來的研發(fā)人員。使用的是“Assign to last role member (JMWE add-on) ”插件提供。
這個Project Role,是需要我們在項目設置的。
可以在項目設置中這個菜單下進行設置。
Bug總結
Bug的整體邏輯相對來講簡單很多,主要是方便了研發(fā)和測試之間的流轉,并且盡量保證轉換時人員分配的自動化。
默認任務
默認任務這里不再過多說明,正常的“待辦-進行中-完成”的流程即可滿足。
總結
至此,所有的工作流設計介紹完成,其中涉及到腳本的部分都是使用Groovy語言編寫,其中最難的部分還是在于確認如何獲取需要的字段在Groovy中的獲取方式。
主要還是通過這里的一些幫助工具來進行探索。