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