年単位で停留していた課題を「対話型ファシリテーションの手ほどき」参考に棚卸しした記録
クラスメソッドは6月が期末です。今年も6月が近いので、社内でのサービス開発室への問い合わせや提案に関するチケット棚卸しを始めました。
チケットが残る原因は大体以下の通り。
- ボールをサービス開発室が持っている
- チケットのステータス更新停止
サービス開発室持ちとなっている場合は関連Issue等で進捗を確認しますが、大体は優先度の関係で進んでおらず、据え置きとなることが殆ど。
問題は後者のチケットで、担当者へのヒアリングが必須です。厄介なのは数年単位で留まっているケース。切り出し方も悩ましくなります。
今回の棚卸しで実践したことを含めて、棚卸しまでの完了例として書いてみました。
棚卸し対象の確認
サービス開発室で棚卸し対象になるチケットはBackLogでステータス管理しているものとしています。ステータス管理はデフォルトのものをそのまま使っており、そのうち、「未対応」「処理中」が棚卸し候補です。「処理済み」状態のチケットを毎週の定期自動棚卸しにて「完了」扱いにしているため、「処理済み」への変更が目標となります。
今回の棚卸し一覧表示はAPIを使って取得しました。棚卸し一覧は期間で絞ればブラウザ上での一覧表示でも十分カバーできそうですが、実はステータス指定に問題があります。URLのパラメータを見るとstatusId=1&statusId=2&statusId=3
がセットとなっており、未対応=1、処理中=2、処理済み=3が固定で処理済みを除外することができません。
API呼び出しに使ったスクリプト自体は毎週の定期自動棚卸しで用いているものを少し改変したバージョンとなります。
var exports = exports || {}; var module = module || { exports: exports }; function onOpen() { var ui = SpreadsheetApp.getUi(); // Uiクラスを取得する var menu = ui.createMenu('追加メニュー'); // Uiクラスからメニューを作成する menu.addItem('BackLogチケット取得', 'getBacklogTickets'); // メニューにアイテムを追加する menu.addToUi(); // メニューをUiクラスに追加する } function getBacklogTickets() { var ss = SpreadsheetApp.getActiveSheet(); ss.clear(); var prop = PropertiesService.getScriptProperties(); var apiKey = prop.getProperty('BACKLOG_API_KEY'); var spaceId = prop.getProperty('BACKLOG_SPACE_ID'); var projectId = prop.getProperty('PROJECT_ID'); var BACKLOG_ISSUE_ENDPOINT = "https://" + spaceId + ".backlog.jp/api/v2/issues"; var BACKLOG_VIEW_URL = "https://" + spaceId + ".backlog.jp/view"; var s = new Date(2020,1,1,0,0,0); var since = Utilities.formatDate(s, "Asia/Tokyo", 'yyyy-MM-dd'); var _ = new Date(); var n = new Date(_.getFullYear(), _.getMonth() > 1 ? _.getMonth() - 1: 1, 1, 0,0,0) var until = Utilities.formatDate(n, "Asia/Tokyo", 'yyyy-MM-dd'); var res = UrlFetchApp.fetch(BACKLOG_ISSUE_ENDPOINT + "?apiKey=" + apiKey + "&projectId[]=" + projectId + "&sort=created&createdSince=" + since + "&createdUntil=" + until + "&statusId[]=1&statusId[]=2&count=100"); var contents = JSON.parse(res.getContentText()); var headers = ['件名 (' + Utilities.formatDate(new Date(), 'JST', '実行時刻: yyyy-MM-dd HH:mm:ss )'),'キー', '状態','担当者','依頼者', '種別', '作成日', 'URL']; var values = contents.map(function (info) { return [info.summary,"=HYPERLINK(\"" + BACKLOG_VIEW_URL + "/" + info.issueKey + "\", \"" + info.issueKey + "\")", info.status.name, info.assignee ? info.assignee.name: "", info.createdUser.name, info.issueType.name, info.created.split("T")[0], BACKLOG_VIEW_URL + "/" + info.issueKey]; }); values.unshift(headers); ss.getRange(1, 1, values.length, headers.length).setValues(values); }
部外へのヒアリング
ポイントは、チケットを発行した人の特定可否です。問題は該当スタッフが異動、あるいは退職されているケースでしょう。
個人特定が出来た場合、Slack社内向け質疑用チャンネルにてメンションを送っています。セキュリティや個人情報が絡む場合を除いて、第三者確認のためDMにはしていません。
特定出来なかった或いは在籍が確認できなかった場合は所属部署全体へのメンションを行いますが、部署によっては基本使われないところもあること、それらの区別は即付くわけではないことにて投稿時にややストレスは掛かります。
メンションの文面も悩ましくなります。チケットが残る時点で何かしらのトラブルがあったと想定するものですが、原因もわからず無闇に非難すべきではありません。今回は以下の書籍を参考にしました。
事前に社内の一部チャンネルで話題に上がっており、何名かが注文している中で私も注文していました。
実際に投下したメンションは以下の通り。
当初は「なぜ更新されていないのか」といった感じのフレーズを考えましたが、今回の目的は棚卸しがなされることになるので、該当フレーズはカットしました。2年前の見落としチケットです。原因なんて忘れてる可能性は大いにあります。「なぜ更新されなかったのか調べる」という解決し難いチケットが追加発生する可能性もありますし、担当者が異動や退職されている場合には年単位での棚卸し対象になりかねません。
念の為チケット内コンテキストを抜粋してSlackに投げることも考えましたが、必要なコンテキストが漏れ落ちる可能性を考えて、丸ごと参照してもらう想定でチケットのURLのみポストしています。
結果として、オペ部のIssue管理ステータスは解決更新されていたものの、サービス開発室での管理ステータスが追従出来ていなかったというものでした。Issueそのものは解決できていたので問題なしとしました。
サービス開発室内の進捗確認
開発室内でのやりとりの場合、大体は該当メンバーへのメンションを送信するのみで完了します。退職されている場合、事前にチケットの棚卸しも完了されているケースが殆どで、基本は室の在籍メンバーが対象です。
GitHub内で発行したIssue URLをチケットにコメントする手順となっているため、それらを追うだけです。たまにコメントに書き忘れているケースもあるので、念の為リポジトリ内を検索しておきます。ARN等が判っている場合はそれを、チケットの日付以降のコミットを辿ることもあります。
当のメンバーに聞けば直ぐ分かるかもしれませんが、添記されているコメントやコミットのURL等を検索することで、別途管理している手段やページに辿りつくこともあるためです。
一通り確認し終えたら、該当メンバーへのメンションを送信します。文面は「こちらのチケット既に対応完了済みであれば処理済みに変更お願いします。」で問題ないでしょう。
今回は該当チケットに関して上記のようなメンションを送信し、ステータスが「処理済み」へ更新されたことで完了としました。
あとがき
棚卸しで一番の課題は、時間が大きく経過してしまった課題をリマインドする際の文面です。特にボールが相手側にある場合、進んでいないことを非難するような文面にしてしまいがちですが、結果として言い訳のステップに入る可能性もあります。この辺りの対策については、記事中で挙げた「対話型ファシリテーションの手ほどき」をおすすめします。