ContractS開発者ブログ

契約マネジメントシステム「ContractS CLM」の開発者ブログです。株式会社HolmesはContractS株式会社に社名変更しました。

プロダクトマネージャーとしてのヒアリングスキル

こんにちは。Holmesでプロダクトマネージャーをやっている井上と申します。 今回の記事は Holmes Advent Calendar 2020 - Qiita 17日目の記事です。

はじめに

プロダクトマネージャーのスキルには実に様々なものがあります。調べるとよく出てくるのが下記のプロダクトマネジメントのトライアングルにあたるものです。

f:id:yinoue22:20201223202309p:plain
https://productlogic.org/2014/06/22/the-product-management-triangle/

もちろん全て欠かすことのできない重要な要素ではありますが、そのような中でも特に重要なものとして私が考えているのは、「ユーザヒアリング」です。(上記の図でいうところの「User Research」にあたる箇所になります。)

ユーザヒアリングの目的について

何故「ユーザヒアリング」を行う必要があるのか。 それはいかにお客様が抱えている課題を聞き出し理解し、その上で機能として実装するのかどうかを判断するためです。ここはひとつプロダクトマネジメントにとって大きな観点になります。

今回はその「ユーザヒアリング」を行う上で意識すべき点について書いていきたいと思います。

プロダクトマネジメントの文脈で書いてはおりますが、これは必ずしもプロダクト開発のみならず、どんな種類の仕事を行う上でもはたまた私生活にも十分応用できる考え方になります。

なお、内容についてはほとんどこちらの書籍を参考にしているのでご興味ある方がいれば是非読んでみてください。

「対話型ファシリテーションの手ほどき」

悪いヒアリング

『私たちは、「なぜ?」と聞かれるとつい「言い訳」をするようにできているのです。』

いきなりどきっとする記述がこちらの本の中にあります。

ヒアリングにおいて気をつけるべきことはこの一文につきると思います。 なぜと聞かれるとどうしても責められているように人間は受け取ってしまい、ちょっと濁して答えてしまったり言い訳のようなことをいってしまったりついついその場を逃れようとすぐに謝ったりします。皆様もご経験あるのではないのでしょうか。 (かく言う自分も「なんで?」「どうして?」と聞かれることが非常に苦手です・・・) そして本当に聞きたかったことが全然聞くことができなかったということになりがちです。

お客様へのヒアリングにしても往々にしてこういったことが発生しがちだなと思います。

私:「最近なかなかホームズクラウドを利用できていないみたいですね。どうしたんですか?」

お客様:「いや、ここのところちょっと忙しくて・・・」

私:「そうだったんですね、なんで最近忙しくなってきたんですか?」

お客様:「やらなきゃいけない業務もたくさんありますし・・・」

私:「そうですか、それは大変ですね。いつごろなら落ち着いてきそうですか?」

お客様:「うーん、なんとも言えないところですね・・・」

私:「そうですか、ではまた進捗確認しに打ち合わせさせていただきますね。」

上記の通り、実態はなんで忙しかったのか、どうすれば使ってもらえそうなのかといったことが何も分からず終わってしまいました。

おそらくこの調子だと次回の打ち合わせを行っても特に進捗はないでしょう。

青文字で書いてある部分が悪いヒアリングの部分です。その後事実確認をしようと赤文字の質問をしてはいますが、お客様からするとすでにかなり嫌な気分になっていると思われるのでここの回答も曖昧になってしまっていますね。

よく「なぜなぜ分析」をやったけど問題解決にならなかったというのは、なぜと聞くこと自体が本質的に感情に訴えかけているものなので、どこかに感情が入り込んでいるからだと思います。 問題解決のためには「なぜ」を分析すべきだ!というのはよく分かるのですが、上記の通りコミュニケーションが特に重要な場においては逆効果である可能性もあります。 (そもそもなぜなぜ分析は自分が関わる課題に対してやるのがよく、お客様に対してなぜなぜ分析をしたらおそらく嫌われると思っています笑)

良いヒアリング

では良いヒアリングとはなんでしょうか。

「なぜと聞いてしまうと人はついつい言い訳をしてしまう」 ということであれば、「なぜ」「どうして」を聞かずにとにかく、「何」「いつ」「どこ」「誰」を尋ねていくことと本の中にはあります。 そうやって事実確認のみを行いひたすら事実を積み上げていって、何が問題か何が課題なのかをこちらが推測で立てるのではなく、相手に自ら気づいてもらうことです。 そして本当に解決しなければいけない課題が浮き彫りになってきます。

先ほどの悪いヒアリングを良いヒアリングに置き換えるとどうなるでしょうか。

私:「最近なかなかホームズクラウドを利用できていないみたいですね。何かつまづいているところがありますか?」

お客様:「いや、つまづいているというよりは単純に時間がとれていないって感じですね。」

私:「そうだったんですね、時間がとれていないのは具体的にどなたになりますか?営業の方ですか?法務の方ですか?」

お客様:「うーん、主に法務部、というか私が今は立て込んでいてなかなか時間がとれていないですね。」

私:「そうですか、それは大変ですね。何の業務で忙しいんですか?」

お客様:「今は営業からの契約レビュー依頼がとてもたくさん来ているという状況で、、結構困っているんです。」

私:「主に困っている点はでしょうか?単純にレビュー自体に時間がかかるということでしょうか。それとも数が多く来すぎて把握・管理に時間がかかってしまうということでしょうか。」

お客様:「それでいうとまずは部長である私のもとにレビュー依頼は一旦寄せられるのですが、数が多いのとその依頼がメールで来るのでなかなか全体像が把握しづらいんです。ときには抜け漏れも発生してしまいそのことでまた営業から問合せがきて再度やりとりが発生して。。レビュー依頼の把握と担当者への割り降り等が煩雑になってきており、そこの管理に時間が取られてしまっているという状況です。」

私:「そうでしたか、ありがとうございます。」

この通り事実確認を積み重ねることにより、「業務が多忙でホームズクラウドを使っている暇がない」という認識から、業務多忙が実は課題ではなく「レビュー依頼の数が多く管理がしきれず把握に時間がとられてしまう、ときには抜け漏れも発生してしまうなど、コミュニケーションコストも大きく発生している。」という課題を掴むことができました。

そうすると後は、「各方面から来ているレビュー依頼について簡単に把握でき効率的に最適な担当者にさばけるようになる」ということは実現できればこのお客様はホームズクラウドを利用することで課題解決につながり、活用率も向上していくことでしょう。

さいごに

ユーザヒアリングは直接お客様の課題感を感じられる良い機会である一方、きちんと準備をして臨まないと単に時間の無駄になることも多いです。こちらが一方的に聞きたいことだけ聞こうとすると相手は萎縮してしまったり、意図したことが聞けなかったりということもあります。 そこで相手がいかに答えやすい形で事実確認の質問をするかが重要になってきます。

とつらつら書きましたが自分自身ももちろんまだまだ実践できてはいないため、今後も強く意識して頑張っていきたいところです。

参考書籍:「対話型ファシリテーションの手ほどき」

スプリントレビューでプロダクトの価値を高める

この記事は Holmes Advent Calendar 2020 - Qiita 12 日目の記事です。

こんにちは。 Holmesでスクラムマスターをしている吾郷です。

今日は自分が所属するHudsonチームで行ったスプリントレビュー改善について書きます。

背景と課題

Hudsonチームでは現在のプロジェクトに関わるステークホルダーを毎週スプリントレビューに招待しています。 様々な部署からご参加いただき、ステークホルダーの数は今や20名以上となりました。

組織の拡大に伴い参加人数が大きくなっている中で、ステークホルダーに求めていることや参加スタンスの統一が難しくなり、開発側からは「本来のスプリントレビューの目的を達成できていないかもしれない」、また、ステークホルダーからは「実際に何を質問してよいかわからない」といった声も上がっていました。 また、スプリントレビューを含める開発プロセス全体がステークホルダーや開発者内で不透明であることも課題のひとつでした。

そんな中、スクラムマスターが集まる会議で組織として上記のレビュー課題を改善していこうという流れになり、各チームでそれぞれスプリントレビュー改善を行う事となり、今回のワークに至ります。

やったこと

今回のワークは

  1. チーム内での理想とするプロダクト開発の流れの認識合わせ
  2. 現在のスプリントレビューに対する振り返り
  3. 振り返りからいくつか分類分けを行い、本来の理想の姿や大切にしたいことの洗い出し
  4. それを実現するためのTRY出し

といった流れで実施しました。

結果

ワークの結果

f:id:seiseiholmes:20201217191608p:plain 簡単ではありますが、生まれたTRYと解決したい課題を紹介していきます。 PO・SM・開発者それぞれで誰が行うかというところまで決めたTRYが生まれています。

スプリントレビューの目的資料を一枚挟む
  • 担当:スクラムマスター
  • 内容:スプリントレビューの始めにステークホルダーの方に向けて、このイベントの目的や求めていることを伝える。
  • 背景:参加者の方々とスプリントレビューの目的の認識合わせができておらず、何を発言していいかわからないという状況。スクラムマスターがスクラムガイドにある通り、組織へのスクラムの適用を実現する。
PBとFBの状況を共有する
  • 担当:プロダクトオーナー
  • 内容:スプリントレビューの始めに、現在のプロジェクトの概要・今回のスコープ・過去のFBの状態を共有する。
  • 背景:意見が活発にでない理由の一つに、出た意見の状態やどういう扱われ方をしているのかステークホルダーの方に不透明であることが原因ではないかと考えました。そこで、スプリントレビューで明確に伝えることで、意見を言う意義があることを示します。
ユースケースに沿ったデータの作成
  • 担当:開発者
  • 内容:ステークホルダーの方にイメージしてもらいやすいように、登録する登場人物などの情報を具体的なものにしておく。
  • 背景:よく開発内のテストでも、アカウント名を"テスト"としたりすることが見受けられました。その状態でデモを行っても、ステークホルダーの方には、運用イメージがわきにくく、意見も出にくいといったことがありました。

TRYの結果

今回は、TRY実施後の成果指標としてFBの数と種類を各チームで集計し計測することとしました。 まさに、プロダクトの価値向上に寄与するFBをいくつ引き出せたか、いただけるかといったところを指標としました。

約一ヶ月半ほど計測してみた結果です。(Infinity、Trinityは別チームの名称です。) f:id:seiseiholmes:20201217192302p:plain

TOが行き先です。簡単に定義を説明します。
Close・・・その場で回答し、解決したもの
PB・・・プロダクトバックログに追加となるもの
Research・・・プロダクトバックログに追加すべきか調査が必要なもの
Sprint・・・スクラムチームで検討すべきもの

二枚目は、スプリントレビューごとの推移です。 f:id:seiseiholmes:20201217192811p:plain

総計だけでみると判断しづらいかもしれませんが、推移で見るとチームのFBの変化が確認できると思います。 またHudsonでも、PBやResearchの分類も増えてきており、組織としても非常にいい形で動き出したように考えられます。

まとめ

FBを引き出す技術というのは、認識合わせや求めることの共通理解が必要なため大変難しいことだと感じています。 しかし、今回のワークでは改善の方向に進むことができました。 まだまだ開発としては課題も多いですが、チームないし組織として連携し、改善をしていきたいと思います。 実際にチーム間でも、レビュー用のテンプレートを用意したりといった動きもありました。

Holmesでは、スクラムを用いながらプロダクトを成長させ、契約に関する顧客課題解決を通じた価値提供をしていきます。 興味がある方はご連絡下さい。

lab.holmescloud.com

スプリントプランニング〜虎の巻

この記事は Holmes Advent Calendar 2020 - Qiita 9 日目の記事です。

こんにちは。 Holmesでスクラムマスターをしている吾郷です。

今日はチームで行ったスプリントプランニング改善について振り返ります。 また、それにあたって最近アップデートがあったスクラムガイドも参考にしましたので、そちらにも触れながら自身の所属チームのHudsonのスプリントプランニングについて紹介します。

スプリントプランニング改善したい!という意見が出てきたのでチーム内でプランニングの目的や、やることを再定義する機会を設けました。

背景

自分が参加しているスクラムチームはHudsonといいます。 今年の1月からHudsonというチームで活動を続けています。 この一年の間で様々なTRYが生まれ、各イベントやスプリントで実施してきました。

課題

現在、スプリントプランニング内での実施項目が多く、アジェンダが曖昧。 (特にスクラムガイド2017内でいうプランニング二部) そのため、時間が足りなくなり、目的とするスプリントバックログ作成までできていない。

やったこと

スクラムガイドがアップデートされていい機会なので、きちんとガイドに沿うために読み合わせをする。 30分でトピック1からトピック3を読み、それぞれ気づいたことを共有・ディスカッション

結果

今回は、普段使用しているオンラインホワイトボードのmiroに整理したアジェンダを作ってみました。 結果としてはHudsonチームでは以下の分割・内容でプランニングを進めてみることにしました。 時間はトピック1と2に30分ずつ、トピック3に1時間を目安にしています。

f:id:seiseiholmes:20201214180248p:plain

トピック1:Why
プロダクトの価値の提案 from PO

基本的にはスクラムガイド2020のトピック1をそのまま採用しています。 まずはPOからプロダクトバックログの状況などを共有してもらいながら、どういったことがこのスプリントでできるとよいかを提案してもらい、 その提案をもとにスクラムチームでどういうスプリントゴールを設定するか議論します。

スプリントゴール決め(仮)

一通り意見が固まれば、仮案としてのスプリントゴールとして次のトピックに進んでいきます。

トピック2:What
アイテムの選択

直近のベロシティを確認しながら、どのアイテムを選択するかを話し合います。 また必要であればPBIをスプリントのサイズに分割したり、実装についての認識合わせをしたりといったリファインメントも行います。

Hotfix

また、現在HolmesではHotfixチケットについて、専任チームではなく状況に応じて各チームで対応を行っています。 そのため、対応すべきHotfixがなにかの認識合わせも行います。

スプリントレビューフィードバック検討

さらに、このトピック2でスプリントレビューでいただいたフィードバックの対応方針検討も行います。

トピック3:How

ここからは、各スクラムチームで色が分かれるところになると思いますので、参考にしていただけると幸いです。 基本的には、スプリントバックログを作成していく時間になります。 せっかくなのでアジェンダ内のTRYについても少し触れながら流れを説明します。 気になるアジェンダなどありましたら、ぜひコメントしていただければと思います。

モック確認・タスク作成

モックを確認しながら、現時点で必要な作業をまずは付箋に洗い出していきます。 この際、モックも必要なことがわかれば付箋にして作成していきます。

マッスルマーク

一通りの作業を洗い出したら、各曜日の午前午後に自分たちがスプリントに集中できるかどうかを一見してわかるようにマッスルマークというものを振っていきます。力こぶのようなものがそれです。下記画像参考

f:id:seiseiholmes:20201214185157j:plain

クラス名決め

さらにプランニング時点で想定できるクラス名を決めてしまいます。 ここで決めれば都度都度悩む必要がなくなりますし、誰でもすぐに作業に着手できるためです。

SBIの時間ふり

このあと、各付箋に予定時間をmiroのタグ機能を用いて設定していきます。 これによって、各曜日に置く付箋の量などを調整しやすく、 スプリント内における残タスク量も可視化しやすくなり、適応を促進してくれます。

ロードマップ調整

時間まで入力を終えるといよいよ最後にロードマップ決めとなります。 これまで洗い出した付箋にかかれた情報とカンバンをもとに、 一週間の中でどのようにスプリントゴールを達成するかを決めます。

スプリントゴール最終確認

ここで仮に想定していたスコープでは難しい・違ったといった気づきがあれば、量の調整やスプリントゴールの調整を行います。

以上が、スプリントプランニングトピックでHudsonが行っているものです。

まとめ

ガイドラインが新しくなり、自分もチームで一緒に読める機会が持てたのは非常に良かったです。 他のセクションに関しても、チーム内ないし開発内での読み合わせを行っていきたいです。

また、今回のワークでスプリントプランニングに対する共通認識を作れましたが、今後の経験を通してこのアジェンダも変化していくことと思います。 さっそく、定義後のスプリントで実践してみて、トピック3で時間が足らないなどのこれからの課題も見えてきています。

定期的にどういったプランニングをしているか続編としてブログにしたいと思います。

Holmesでは、スクラムを用いながらプロダクトを成長させ、契約に関する顧客課題解決を通じた価値提供をしていきます。 興味がある方はご連絡下さい。

lab.holmescloud.com

Haskellの型を触ってみた

この記事は Holmes Advent Calendar 2020 - Qiita 16 日目の記事です。


こんにちは。Holmesでインフラエンジニアをしている渡辺です。

Holmesでは社内LT大会が何度か開催されていて、趣味の話や技術話など業務に関係あることないことを持ち寄り、みんなそれぞれ好きなことを話しています。自分も発表に参加したので、ここではその時に自分が発表した内容を紹介できればと思います。

エンジニアであれば、自分が好きなプログラミング言語があるという人は多いと思います。自分が好きな言語はいろいろありますが、Haskellが好きなので今回はそれについてのお話です。

型について

大抵のプログラミング言語で扱える型としては以下のような物があると思います。

-- 文字列型
String s = "Hello, World"

-- 数値型(整数)
Integer x = 256

-- 数値型(浮動小数点数)
Double d = 3.14159265

-- 論理型(ブーリアン型)
Bool b = true

-- 配列型
List xs = [2, 3, 5, 7, 11]

通常取り扱うデータとしてはこのような型があれば普通のプログラムを書く分には困らないでしょう。ですが、例えば成功・失敗を表したい時にはどうするでしょうか。関数の結果として返り値で取得したい場合には構造体などのデータ構造を用いて取得する場合もあると思います。もしくは例外処理で対応することも可能でしょう。Haskellでは次のような書き方を使うこともできます。

Maybe型

失敗を返すかもしれない関数の並びから計算を構築できます。失敗はNothingで表現し、成功はJust a (aは任意の型)で返すことができます。

data Maybe a = Nothing | Just a

-- x が 2 で割り切れない場合には失敗する
div2 :: Int -> Maybe Int
div2 x = if even x
            then Just (x `div` 2)
            else Nothing

使用例は下記の通りです。ghciでの実行例になります。

ghci> :t Just 123
Just 123 :: Num a => Maybe a

ghci> :t Nothing
Nothing :: Maybe a

ghci> div2 6
Just 3

ghci> div2 3
Nothing

Either型

失敗あるいはエラー処理を構造化する例外処理をつかう関数のならびから計算を構築できます。失敗時にもただ終了するのではなく、エラーの内容から次のアクションを設定することができます。

data Either a b = Left a | Right b

head' :: [a] -> Either String a
head' [] = Left "empty list"
head' (x:xs) = Right x

使用例は下記の通りです。

ghci> :t Right 123
Right 123 :: Num b => Either a b

ghci> :t Left "failure"
Left "failure" :: Either [char] b

ghci> head' [1,2,3]
Right 1

ghci> head' []
Left "empty list"

さて、通常の型の使用方法としてはこれぐらいにして、もっと型を駆使して面白いことはできないでしょうか。

型レベル計算

Haskellは型の表現力が高く、型レベル自然数という物が扱えます。

まずは下準備として、今回の記事の書き方を使うにあたって必要なGHCの機能を有効化します。

{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, UndecidableInstances, FlexibleInstances #-}

型でペアノ数を表していきます。Z は 0 を表します (zero)。S は (+1) を表します (successor,後者)。S Z は 1 、 S (S Z) は 2 という意味になります。

-- ペアノ数の定義
data Z
data S a

型クラスとそのインスタンスで関数を定義しています。

-- 型レベルの演算として加算を定義
class Add a b c | a b -> c

-- 0 + a は a になる
instance Add Z a a

-- a + b の結果を c に入れる
instance Add a b c => Add (S a) b (S c)

足し算(add), zero, oneについては型のみで表現し、two, threeについてもそれらの組み合わせでできています。

add :: Add a b c => a -> b -> c
add = undefined

zero = undefined :: Z
one = undefined :: S Z

two = add one one
three = add one two

ここまで書くと実際に型での計算が確認できるようになります。

ghci> :t one
one :: S Z

ghci> :t two
two :: S (S Z)

ghci> :t three
three :: S (S (S Z))

というわけで、型をうまく扱うことで型の上で数字の取り扱いができるようになりました。 今回の内容ではあまり高度な例とも実用的な例とも言えないかもしれませんが、型の表現力の一端は感じていただけたのでは無いでしょうか。

感想

業務では使用していないプログラミング言語なので難しかったという声はありましたが、内容を理解したうえで面白かったと言っていただけたり、Haskell関数型言語に興味を持ってくれた人がいたのでとても有意義で楽しい社内LT大会になりました。

最後に

Holmesはエンジニア・デザイナーを募集しています。 興味がある方はぜひこちらからご連絡ください!

lab.holmescloud.com

lab.holmescloud.com

Nuxt.jsのビルドを高速化してみる

こんにちは。Holmesでエンジニアをしている三澤です。

背景

弊社ではフロントエンドフレームワークとしてNuxt.jsを利用しています。プロジェクトが進みコードが増えてくるとビルドに時間がかかってくるのが悩みの種です。 ビルド時に最も時間がかかっているのは下記の画像の部分ですが、恐らくwebpackのビルドに最も時間がかかっているのではないかと仮定しました。

f:id:a-misawa:20201214150456p:plain

この仮定に基づいて何かいい方法はないかと探しているとNuxt.js本家のGithub上で興味深いissueを見つけました。

github.com

ここでNuxtの中の人がこのようなことを言っていました。

f:id:a-misawa:20201211223733p:plain

今回は上記のコメントで取り上げられている方法を試してNuxt.jsのビルド高速化(=webpackのビルド高速化)に取り組んでみたいと思います。

概要

今回行うビルド高速化は、nuxt.config.jsのbuildプロパティに設定を追加することによってwebpackのビルド設定を変更します。 nuxt.config.jsのbuildプロパティに追加する項目は下記の3つです。

名前 内容
parallel webpackプラグインのthread-loaderで複数スレッドでビルドすることで高速化する
cache webpackプラグインのterser-webpack-pluginでjsをminifyし、cache-loader を使ってwebpackビルド時のloaders情報をキャッシュ化して高速化する
hardSource webpackプラグインのhard-source-webpack-pluginでモジュールをキャッシュ管理することにより2回目以降のビルドを高速化する

実行結果

何もしていない場合

  • 平均ビルド時間:53秒

私の環境で何回か実行した場合の平均値ですが、この数値をどれだけ低くできるかになります。

parallelプロパティ

https://nuxtjs.org/api/configuration-build#parallel

nuxt.config.jsに下記1行を追加します。

export default {
  build: {
    parallel: true, //<-追加
  • ビルド時間(1回目):1分11秒

  • ビルド時間(2回目):1分08秒

却って遅くなるという結果になりました。この設定はファイル数が多い場合に有効なようですので今回は不要ということになります。

ファイルが多い場合どの程度短縮されるかについての参考情報:Webpackのビルド時間を1/3にした話 #gotandajs - Speaker Deck

cacheプロパティ

https://nuxtjs.org/api/configuration-build#cache

nuxt.config.jsに下記1行を追加します。

export default {
  build: {
   cache: true, //<-追加
  • ビルド時間(1回目):49秒

  • ビルド時間(2回目):42秒

こちらは1回目からビルド時間が少し短縮され(terserのおかげ?)、2回目以降もcache-loaderが効いているからか更に短縮されました。

hardSourceプロパティ

https://nuxtjs.org/api/configuration-build#hardsource

nuxt.config.jsに下記1行を追加します。

export default {
  build: {
   hardSource: true, //<-追加
  • ビルド時間(1回目):1分43秒

  • ビルド時間(2回目):21秒

こちらは1回目はモジュールのキャッシュ作成のため大幅に増えてしまいましたが、2回目以降は平均20秒前後でビルドできるようになりました。

上記3項目すべて

nuxt.config.jsに下記3行を追加します。

export default {
  build: {
   parallel: true, //<-追加
   cache: true, //<-追加
   hardSource: true, //<-追加
  • ビルド時間(1回目):49秒

  • ビルド時間(2回目):21秒

1回目からcacheプロパティが効いているためか短縮されました。2回目以降もhardSourceプロパティのおかげで大幅に短くなっています。

感想

体感的にはhardSourceプロパティが一番効果があり、次いでcacheプロパティが効くことが分かりました。parallelは今回効果がなかったようですが、ファイル数によってはビルド時間を大幅に削減できるようです。

デメリットとしてはhardSource、cacheプロパティ共に未だNuxt.jsとしては実験的機能の扱いとなっていることです。 調べてみますとhardSourceをONにしてビルドすると稀にビルドに失敗することがあるようです。解決方法としてはnode_modules/hard_source/*(hardSourceのキャッシュファイル群)を削除するとエラー解消されるようですので、キャッシュ管理に問題が潜んでいるのかもしれません。ですから未だ実験的機能であることから、チーム開発で活用するかは要相談だと思います。

また、今回はプロパティを設定して効果を測定したに過ぎません。今回ご紹介した3つのbuildプロパティはいずれもwebpackのプラグインを利用しているため、webpackの仕組みを理解することがNuxt.jsのビルド時間短縮の本質的な最適化につながりそうです。今後もwebpackのビルドの仕組みを更に調査してみたいと思った次第でした。

webpackのビルドパフォーマンスについての情報:Build Performance | webpack

最後に

Holmesではエンジニアを募集しております。ご興味がある方はこちらからご連絡ください。

lab.holmescloud.com

ArchUnitがDDDのモジュール実現にちょうど良かった話

この記事は Holmes Advent Calendar 2020 - Qiita 11 日目の記事です。


こんにちは。エンジニアの友野です。最近、Quartoにハマっており、チーム内普及に勤しんでいます。

先日、ドメイン駆動設計(以降、DDD)初学者にありがちな

  • 「これはどこに置くんだっけ?」
  • 「このクラスからドメイン層呼んでいいんだっけ?」

というような不安を払拭するために良い方法はないものかと悩んでいたところ、前回のJJUGでArchUnitを知りました。 早速プロダクト適用してみたら、結構良かったのでまとめておきます。

これから例示するコードは以下の動作環境*1で作り、動かしています。

JVM: OpenJDK Runtime Environment Corretto-8.252.09.1 (build 1.8.0_252-b09)
Gradle: 4.10.3
Kotlin: 1.3.41
Spock: spock-spring:1.3-groovy-2.4

ArchUnitとは

ArchUnitは、Java/Kotlinで書かれたアプリケーションのアーキテクチャを検証するためのライブラリです。パッケージやクラスなどの依存関係チェックができます。詳しくは公式サイトをご参照ください。

www.archunit.org

Holmesでは一部アプリケーションをKotlinで書いているので、JavaだけでなくKotlinもテストできるのは嬉しいですね。 詳細なセットアップ手順やAPIは読みやすいドキュメントが公式から提供されているので、割愛します。

www.archunit.org

Why ArchUnit

改めて整理するとやりたいことは、以下の2点です。

  • 参照関係を明確化して責務分離を進める
  • データモデルなどの既存資産とドメインモデルを分離する

パッケージ構造や参照関係のような決め事を守るために、最も簡単な始め方はレビュープロセスで担保することです。 しかし、ご存知の通り、この方法は強制力がなかったり、見逃しがあったり、適切に運用するのは難しいです。

仕組みで守る方法として、gradleでサブプロジェクト化して依存の方向をdependenciesで定義することも考えられますが、新規プロジェクトならいざ知らず、既存資産があるとこのアプローチに舵を切るのは若干尻込みしてしまいます。

そのちょうど中間として、現在の構造を大きく変えずに、かつテストコードによって比較的強い制約として表現できるArchUnitはまさに我々のような状況には最適と言えます。

セットアップ

今回対象としているアプリケーションはkotlinでプロダクションコードを書き、テストコードはgroovy(spock)で書いています。 それぞれのバージョンは冒頭で記載した通りです。 テストフレームワークにspockを採用しているので、build.gradleの依存には無印のものを追加します。

dependencies {
    // other dependencies...
    testImplementation 'com.tngtech.archunit:archunit:0.14.1'
}

spockテストクラスも書いておきます。

class ArchitectureSpec extends Specification {

    final def BASE_PACKAGE = "com.example"
    final String[] LEGACIES = [
        "..entity..",
        "..repository..",
        "..service.."]
    final def CONTROLLER = "..controller.."
    final def USE_CASE = "..usecase.."
    final def DOMAIN = "..domain.."
    final def INFRASTRUCTURE = "..infrastructure.."

    final JavaClasses importedClasses =
            new ClassFileImporter().importPackages(BASE_PACKAGE)

    // ここにテストケースを書く
}

これで準備が整いました。

守りたいアーキテクチャ

先日の記事で触れた三層+ドメインオブジェクトアーキテクチャから試行錯誤をして、今のところオニオンアーキテクチャに近い形に落ち着いています。

tech.holmescloud.com

f:id:a-tomono:20201210231738p:plain
簡略化したアーキテクチャ構成図

参照関係の明確化

ユースケース層はコントローラ層から参照され、ドメイン層はユースケース層およびインフラストラクチャ層から参照されます。

def "ユースケース層はコントローラ層からのみ呼び出される"() {
    given:
    ArchRule rule = classes()
        .that().resideInAPackage(USE_CASE)
        .should().onlyBeAccessed().byAnyPackage(CONTROLLER, USE_CASE)

    expect:
    rule.check(importedClasses)
}

def "ドメイン層はユースケース層、インフラストラクチャ層からのみ呼び出される"() {
    given:
    ArchRule rule = classes()
        .that().resideInAPackage(DOMAIN)
        .should().onlyBeAccessed().byAnyPackage(
            USE_CASE,
            DOMAIN,
            INFRASTRUCTURE,
        )

    expect:
    rule.check(importedClasses)
}

これを実行すると…、テストに失敗しました。

...
    at com.example.ArchitectureSpec.ドメイン層はユースケース層、インフラ層からのみ呼び出される(ArchitectureSpec.groovy:47)
Caused by: java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'classes that reside in a package '..domain..' should only be accessed by any package ['..usecase..', '..domain..', '..infrastructure..']' was violated (9 times):

既存資産のモデル変換は、そのモデル自身にさせていたため、インフラストラクチャ層以外から参照していることを思い出しました。 これは既存資産を活かすことを優先した結果でもあるので、広義でのインフラストラクチャとして扱います。既存資産のモデルを参照元に追加して再実行、テスト成功しました。

final def LEGACY_MODELS = "..entity.."

def "ドメイン層はユースケース層、インフラストラクチャ層からのみ呼び出される"() {
    given:
    ArchRule rule = classes()
        .that().resideInAPackage(DOMAIN)
        .should().onlyBeAccessed().byAnyPackage(
            USE_CASE,
            DOMAIN,
            INFRASTRUCTURE,
            LEGACY_MODELS, // 追加
        )

    expect:
    rule.check(importedClasses)
}

既存資産とドメインモデルの分離

新しいドメインモデルを定義しているので、既存資産に依存はさせたくありません。つい、既存サービスクラスをDIすればもっと早く実装できるのに、という誘惑に駆られますが、これはファットなドメインサービスを生み出し、結果としてドメイン貧血症を引き起こします。なによりビジネスルールが散在したままです。 これを避けるためにテストケースを追加します。

def "ドメイン層は既存資産を利用しない"() {
    given:
    ArchRule rule = noClasses()
        .that().resideInAPackage(DOMAIN)
        .should().dependOnClassesThat().resideInAnyPackage(LEGACIES)

    expect:
    rule.check(importedClasses)
}

これで最低限の労力でやりたいことは満たせたと思います。

ふりかえり

今回のテストを書いていく中で、偶然にも、コントローラがドメインオブジェクトを参照している実装を発見できました。レビュー済みの箇所ではありましたが、やはり見落としはあるようです。 テストでアーキテクチャの制約を表現できるので、構造を頭に入れつつも、個々の機能実装に集中できる感覚がつかめました。

現在はコントローラからのドメインオブジェクト参照は禁止していますが、IDクラスなど、制約を部分的に緩くするパターンも今後考えられます。これは、例えば、特定のインタフェースを実装した値オブジェクトのみ許可する、というようなテストで簡単に実現できそうです。 ただし、なぜこの制約があるのかはしっかりと共有しないとルールを守ることがゴールになり、目的と手段が入れ替わってしまう懸念もあります。

まとめ

スタートしたばかりのDDDにArchUnitで適度な制約を設けて、開発者がモデリングとそのコード表現にフォーカスできている状態に一歩近付きました。ArchUnitはユーザーガイドがよくできているので、色々読みながらプロダクト適用していこうと思います。

Holmesでは、今後もDDDを活用してプロダクトを成長させ、契約に関する顧客課題解決を通じた価値提供をしていきます。 興味がある方はご連絡下さい。

lab.holmescloud.com


明日は、同じチームでスクラムマスターをしている吾郷さんによる「スプリントレビュー改善の記録」です 。

*1:2019年に開発したサービスの環境で試しているため、バージョンは少し古いものです

目標管理制度を一年間運用する中での取り組みと学び

この記事はHolmes Advent Calendar 2020 10日目の記事です。

こんにちは。HolmesでVP of Engineeringとして開発組織の組織づくりや採用を担当している守屋です。
最近はもっぱら子供のクリスマスプレゼントに頭を悩ませています。

この記事では約一年間運用してきた評価制度、特に目標管理の運用について、実践を通して学んだことを時系列に沿ってまとめてみたいと思います。大きく分けると制度の導入をした2020/1Q(1-3月)、定着と拡大を図った2020/2Q-3Q(4-9月)、暗黙知言語化を図った2020/3Q-4Q(9-12月)に区切って記述しています。
制度運用にフォーカスしていますので、制度策定のステップについては触れていません。
アーリーステージでの評価制度と給与決定ロジックについて書かれていた、All Star SaaS Blogの金田宏之さんのウェビナー記事にインスパイアされ、このテーマを選びました。

想定される読者の方

主に開発組織での目標管理・評価制度の運用に悩まれている方はもちろん、チームや組織の方向性が揃わず力が分散してしまっているな、と課題を感じている方のちからになれればと思って書きました。

せっかちな方へ

学んだことを先にざっと書きます。

プロセスをぶらさないこと

始めたことは最後までやる。
制度の運用は挫けそうになることばかりですが、プロセスを守っていなければ公平感を感じてもらうことは決してできません。
期間や対象業務など、意思決定したスコープに対してはぶらさずにやりきることが最初の一歩だと痛感しました。

主観を磨き続けること

評価を完全に定量化し、主観が一切入らない状態にすることは、現時点においては方法が十分には確立されていないと思います。
それゆえ、一人ひとりの評価に直接関わる評価者の主観を磨き続けることが大切だとわかりました。
例えば「Aさんの等級に対して、業務Xの取り組み姿勢、最終成果は期待値に達していると判断できるか?」といった基準は、一次評価者同士での主観の磨き合いを抜きにしては納得感のある評価となりえません。

HRM(Human Resource Management)の知見を最大限取り入れる

口当たりの良さを求め、フレームワークや仕組みを導入した当初からベストプラクティスを捻じ曲げてしまうことの危険性については、システム開発に通ずるところ*1があると思います。
迷ったときには先達の知恵を惜しみなく使い、まずは型を守ることを強く意識しました。
また個人的には数年前ワークショップで同席させていただいた人事コンサルタントの坪谷邦生さんとの出会いをきっかけに、HRMの知見に触れることができました。
結果的に坪谷邦生さんの著書『図解 人材マネジメント入門 人事の基礎をゼロからおさえておきたい人のための「理論と実践」100のツボ』は、この一年間で最も繰り返し開いた本になりました。

www.amazon.co.jp

導入(2020/1Q)

この目標管理・評価制度導入以前は、全社でOKRによる目標設定を行っていましたが、評価制度は存在しませんでした。
評価制度を導入するにあたって、OKRについては、Google re:workの簡潔な定義に明記されている通り、評価ツールではありません。
目標管理と評価制度で別のツールを運用することはこの時点ではまだ現実的でないと判断し、他社の制度などを参考にしつつ、MBOの思想をベースとして目標管理・評価制度を導入しました。

制度の理解醸成と導入の背景説明

最初に取り組んだのは、個々のメンバーへの制度そのものと導入背景に対して腹落ちするまで対話をするということでした。
それまで取り組んでいたOKRの運用については、フォーマットの自由度が非常に高く、成果やコンピテンシーのバランスについてもメンバーごとに大きなばらつきが許容されている状態でした。
しかし、評価制度と一体になった目標管理制度に移行するため、目標の中で成果と各コンピテンシーそれぞれに対する目標設定が必要となり、目標設定の難易度が上がったと感じるメンバーが多くいました。
当然業務時間を圧迫する要因となるため、何のためにこの制度を導入して運用するのか、一人ひとりに理解してもらう必要がありました。
このときは一人あたり約一時間時間を確保し、制度の背景についての説明や具体的な運用イメージを膨らませてもらい、そこから目標設定に入っていきました。

最初の個人目標の設定での悩みどころ

実際に運用する目標管理のシートは、多くの企業で採用されているものと近いものでしたが、初めて経験するメンバーも多く設定に苦戦しました。
特に下記のようなポイントで悩みが多く出ました。

  • 成果目標の定量
  • 個人のビジョンと組織のビジョンがアンバランスな目標
成果目標の定量

成果目標については基本的には定量的な目標値を設定することにしています。(例外もあります)
ありがちではありますが、KPIが自己目的化してしまわないように、定量目標を設定することの意義を伝えていきました。

KPIが自己目的化した例を挙げると、例えば組織目標として「ユニットテストカバレッジをXX%に上昇させる」という目標を設定します。
この組織目標からブレイクダウンして、「個人が該当期間にコミットしたコードにより、カバレッジをxポイント向上させる」といった目標をメンバーが立てたとします。
この目標を追いかけると、業務上の重要性に関係なくテストコードの記述量を増やす方向に行動を起こしやすくなります。
本来組織目標としてテストカバレッジを設定する背景としては、コードの変更容易性を高めることや、testabilityを意識することでコードをcleanにしていきたいというモチベーションが強いと思います。
しかし単純に既存のコードへのユニットテストを増やすことだけにフォーカスしてしまうと、変更容易性が逆に下がってしまうという結果にもなりかねません。
そこで、個人目標としては「チームの作業ブランチからmasterブランチへのマージタイミング」といった意味ある変更の単位で、「CIで計測されるテストカバレッジを最低でも下げない」といった形で定量目標を設定します。
これを実現するためにTDDのスキルを習得したり、チーム内のコードレビューを行うといった行動目標を合わせて設定します。

個人のビジョンと組織のビジョンがアンバランスな目標

前出の坪谷さんの著書では、下記の通りに表現されています。

人間性の尊重と業績向上を同時実現するのが本来のMBOです*2

特に中途採用で、前職においてノルマ管理的な使われ方で目標設定を行われていた場合に、「個人のビジョンについて考えたことがなかった」というケースも少なくありません。
その場合、過去のキャリアの棚卸しやそのときに感じたことの言語化に伴走し、個人としてのビジョンを描くことから始めました。
その上で、個人のビジョンといま時点で持っているスキル、組織が個人に期待する役割を加味して目標のバランスを調整していきました。
(たまたま私の前職の上司がリクルート出身だったこともあり、慣れ親しんでいたWill Can Mustのフレームに寄っています。)

定着〜拡大(2020/2Q-3Q)

Holmesが導入した目標管理制度は評価は半年、四半期で中間評価・再設定を行う制度です。
このため、この期間には中間の目標の立て直しと下半期の目標設定が行われました。
特にこの期間では組織の人数が1.5倍に増えたこともあり、全員分のフォローを一人で行うことが難しくなりました。
そこで一次評価を担うメンバーの拡充と業務の委譲を進めていきました。

コンピテンシーの解釈についての明文化

この評価制度で定義されているコンピテンシー項目の等級に対する定義は、全社で共通の記述になっているため、開発組織のメンバーからすると実際の業務シーンを想像しにくいという課題がありました。
そこで、等級に対する定義を開発組織の業務に近づけた解釈を明文化し、目標設定前に全メンバーに共有しました。
その結果下期の目標設定では、年初の目標設定と比較して格段にスムーズに目標設定ができるようになりました。
具体的には、目標を最終的に承認しているCTOが一人あたりのメンバーとの目標設定に費やした時間が、年初の半分に短縮されました。
全員の目標設定スキルの向上と一次評価者の尽力に感謝しかありません。
特に各人の中で、評価制度のコンピテンシーの各項目への理解が醸成されてきたことが大きく寄与しました。
しかし、この時点で一次評価者同士やメンバー間で、その醸成された理解をすり合わせたり明文化する取り組みはまだできていませんでした。

暗黙知言語化(2020/3Q-4Q)

3Qから4Qにかけての中間評価、四半期目標の設定に際して、一つのチャレンジを設定しました。
本人と一次評価者で練り上げる目標の精度向上を目的に、目標の承認を行うCTOを一人あたりが割り当てられる時間の上限を設定しました。
このチャレンジを成功させるために一次評価者の共通認識づくりに取り組みました。
結果的にこのチャレンジには成功し、その後もコンピテンシーに関わる様々な暗黙知言語化することに取り組みました。
下記はその一例です。

MBO本来の意味と目的

振り返ってみると制度運用開始時点ですり合わせすべきことでしたが、その時点では考え及ばず、遅ればせながらこの時点で言語化を行いました。
過去の私自身も含め、MBOというとExcelで作られた目標管理シートをイメージされる方は非常に多いと思います。
しかしMBOを提唱したドラッカーは、Management by Objectives and Self-controlと定義しており、MBOを「目標と自己管理によるマネジメント」というマネジメントの哲学として位置づけています。
そのため、マネジメント哲学としてのMBOの共通認識を作るため、下記のように明文化し、一次評価者と認識のすり合わせを行いました。

  • MBOは目標を手がかりに自らの仕事をマネジメントできる状態にすることが目的
    • 定められた目標に照らし合わせることで、自らの成果を評価できる状態を目指す
    • self-controlに依る、各人の目標への強い動機づけが引き出されることを目指す

また、self-controlを最大限発揮するために下記のようなパターンを洗い出しました。

  • 本人が目標の設定に深く関わる
    • 一方的に課されるノルマのようなものではない
    • 会社・組織の課題を本人が腹落ちしている
  • プロセスを守る
    • 目標を達成することで評価されることが保証されている
    • 画一的なプロセスで制度が運用される
  • 身近な目標
    • 本人が確信を持って設定した目標であること
    • 面談や1on1のときだけ思い出すような目標ではないこと

一次評価者の役割

一次評価者の役割としては、下記の点にしぼりました。

  • メンバーを正しく見る
    • 事実を正確に把握する
    • 信頼関係を築く
  • 組織の未来を本気で考える
    • 未来を自分の言葉で語る
    • メンバーの疑問を払拭する
  • 主観を磨き続ける
    • 公平感を担保するために、一次評価者同士の主観のすり合わせる

おわりに

長文にお付き合いいただきありがとうございました。
これを書いているまさに今、期末の評価と来季の目標設定に組織全体で取り組んでいます。
まだまだ試行錯誤の最中ですが、約一年間で人も組織も大きく成長できるということを体感できたことが個人的に最大の学びでした。
「それ違うっしょ」というツッコミや、「めちゃわかるわー」といった共感などコメントやメッセージなど、なんらかフィードバックいただけるととても嬉しいです。

開発組織は超絶積極採用中です

ピープルマネジメントに関心があるエンジニアやデザイナの方はもちろん、「マネジメントとかマジ興味が湧かない」という方も、Holmesの目標管理・評価制度では一人ひとりの個性やなりたい姿を全力で応援しています。
少しでも興味が湧いたらぜひ一度カジュアルにお話しさせてください。

lab.holmescloud.com

lab.holmescloud.com