@kyanny's blog

My thoughts, my life. Views/opinions are my own.

採用基準についてトレードオフスライダーを使って議論した

開発者の中途採用をやっていくにあたり、「チームの誰もが採用担当」というポリシーでインタビューやコードテストのレビューなどをみんなでやってきたが、「どういう人を採用すべきか?」についての認識が合わなくなってきたと感じたので、認識を合わせるために議論の場を設けた。議論を進めるためのツールとしてトレードオフスライダーを使ってみた。うまくいくか確証はなかったが、事後にアンケートをとったら過半数からフィードバックをもらえ、全て好意的だった(五段階評価で4と5が半数ずつ)ので、まぁまぁうまくいったのだろうと受け止めて、もろもろ公開します。

使った資料はこちら。

以下、意図とか進め方とか学びとか。

最終的な目標は「採用基準についての認識が合うこと」なのだが、全員の認識・見解が一致することなどありえないと思っていて、むしろ各人の認識がどれくらいズレているのかを明らかにすることのほうが重要だと思っていた。それに加えて、採用を「自分事」と捉えて、「自分はどういう人を採用したいのか?」というスタンスで自分なりの採用基準について考えてもらい、その意見をぶつけ合うことで「我々はどういう人を採用したいのか?」という問いへの回答を模索していく、という機会にしたかった。そこで、十数人のチームメンバーを三名程度の小さいグループ毎に分けて、同じ内容で議論する時間をそれぞれのグループに対して設けた。少人数にすることで「参加者が多くて黙る」ことは回避できるが、参加者が少なくても黙る人もいるので、自分を含むエンジニアリング・マネージャーがファシリテーターとしてまんべんなく話を振ったりもした。

これまで何年か採用活動をしてきた中で、論点はだいたい決まっていた。なので、「何を重視すべきか?」という議論をしても実りは少ないだろうと踏んでいた。何を重視すべきなのかはもうわかっている。問題は、何を最も重視すべきか、ではないだろうか。という仮説を立てて、あらかじめいくつか「重視すべきこと」を挙げておき、それらに優先順位をつける試みを通じて「我々が最も重視しているのは何で、次に重視しているのは何で、したがって我々が求める人物像はこうである」ということを明らかにしていく。これは試してみる価値があるアイデアじゃないか?と思って、視覚的にわかりやすく優先順位をつけるためのツールとしてトレードオフスライダーを使うことにした。

あらかじめ挙げておいた「重視すべきこと」は、

  • Webフロントエンド開発のスキル
  • Webバックエンド開発のスキル
  • コミュニケーション能力
  • マインドセット
  • 英語のスキル

の五つで、本題である優先順位づけの議論に入る前に、他にも追加すべきことがあれば挙げてもらい、それもトレードオフスライダーに含めるようにした。途中から追加されている「ビジネスのスキル」というのは例えば「BtoCサービスの開発経験」のようなもので、「日本語のスキル」というのは非日本語話者のみ当てはまる(日本人における「英語のスキル」と同じ)。

上記のうち「マインドセット」は特に曖昧だが、意図的に含めた。どのような言葉を当てはめるべきか迷ったのだが、その他四つ以外の何かに対する総称として「マインドセット」という言葉を使った。「意識の高さ」とか「やっていき力」などと読み替えてもいいかもしれない。その他四つはいずれも「スキル・能力」であり、ある程度は定量的に計測可能だ。過去の採用活動を振り返って、能力の多寡のみで採用可否を決めてはこなかったよな、と思ったので、我々は完全に定性的な何かも重視しているのではないか?という問題提起も含めて、五つめに挙げた。

いざやってみると、まずコンセプトを理解してもらうのに案外苦労した。優先順位づけはあくまで相対評価に過ぎず、スライダーの目盛りのラベルやら数字やらには大した意味は無いのだ、というのはトレードオフスライダーのコンセプトを説明すれば理解できるだろうと思っていたが、意外とそこに引っ張られてしまう人が多く、議論中に細かくフォローする必要があった。また、「会社の採用計画が不明だと前提となる情報が不足しているので優先順位をつけられない」というような意見も出て、本題とは異なる議論に時間を使ってしまうこともあった。そういうことじゃないんだ、「あなたが仲間に加えたいと思うのはどんな人ですか」という話をしているのだ、というのはそもそも採用活動における大前提だと思っていたが、そこからして認識がズレていたりするというのは大きな発見だった。

グループ間で大きく見解が異なった指標もあれば、概ね見解が一致した指標もあった。似た傾向になった指標については、指標の設定に恣意的なところがあったのは否めないので、その影響が出ているかもしれない。グループ内でも見解が異なる部分もあった。これは想定内で、見解が異なる部分について「なぜこの指標の優先順位が他と比べて高い・低いのか」という話をお互いがすることこそが狙いだったので、むしろ見解が異なるところがあってよかったともいえる。

トレードオフスライダーはあくまで議論のためのツールなので、たったひとつのトレードオフスライダーを作ることはそんなに重要ではないし、そもそも統一見解に至ったわけではないのだが、それでもあえて乱暴にまとめると、以下のようになった。

我々は現時点では、おおざっぱにいって

  • マインドセットを何よりも重視する
  • 次いでコミュニケーション能力を重視する
  • WebフロントエンドとWebバックエンドの開発スキルはその次くらいに重要だが、甲乙つけがたい
  • 英語力は開発スキルほど重要ではない
  • ビジネスの経験などは英語力よりもさらに重要度が低い

という採用基準を設けている、といえる。2017年11月現在は。

このトレードオフスライダーは状況によって変わりゆくものだし、個別の採用においても絶対的な指標になるわけではない。それぞれの指標の優先順位が入れ替わることもあるだろう。それで全然構わないと思っている。「コミュニケーション能力にやや不安を感じるが、それを補って余りあるほどの高い技術力があるので採用する」というような判断が下されるのはまったくもって正しい。その判断をするときに、「じゃあどれくらい技術力が高ければ『補って余りある』といえるんだっけ?」という議論をする必要があり、そのためのものさしとして使うことができれば、このトレードオフスライダーは役目を果たしたといえる。

最大の学びは、月並みだが、「フェイス・トゥ・フェイスで話し合うのってめっちゃ大事」ということ。なんでも GitHub と Slack でコミュニケーションをとって解決していくのを美徳とすら考えてやってきたけど、肉声の威力を思い知った。あと、こういうコミュニケーションは面倒くさがらず丁寧に手間と時間をかけてやらないといけないのだな、というのも学びだった。

追記

トレードオフスライダーのテンプレートを作った

仕事で使う資料にトレードオフスライダーを使おうと思ったが、意外にもテンプレートの類が簡単に見つからなかったので自作した。

f:id:a666666:20171030002120p:plain

Google スライドで作って会社の G Suite アカウントのスライドテンプレートとして登録したが、個人用の Google アカウントではそういうことはできないようなので、 Microsoft PowerPoint (.pptx) 形式と OpenDocument Presentation (.odp) 形式でダウンロードしたものを GitHub にも置いておいた。

github.com

recompose の withState

recompose の withState がわからなかったけどブログ記事とコードを読んだらなんとなくわかった(たぶん)。

公式の API ドキュメントのこのサンプルコードがわからなかった。

https://github.com/acdlite/recompose/blob/master/docs/API.md#withstate

const addCounting = compose(
  withState('counter', 'setCounter', 0),
  withHandlers({
    increment: ({ setCounter }) => () => setCounter(n => n + 1),
    decrement: ({ setCounter }) => () =>  setCounter(n => n - 1),
    reset: ({ setCounter }) => () => setCounter(0)
  })
)

withState の第二引数には stateUpdaterName を渡す。 次のステップの withHandlers の中で stateUpdaterName で名前を指定した関数、この例では setCounter が利用可能だが、 setCounter 関数自体は定義されてないのになんで利用できるの? setCounter 関数の実装はどうなってるの?というのが疑問だった。

その疑問が解決するきっかけはこのブログ記事にあった。

tech.ryukyu-i.co.jp

withStateを用いることでstateとそれを更新する為の関数を追加することができます。

props.msgはwithStateの内部のthis.stateであり、setMsgではsetStateが呼び出されているので、再レンダリングが可能です。

ここで、「もしかして setCounter って、 setState({ counter: }) 的なことをする関数で、 withState の内部で自動的に定義されるのか?」と思いついて、 withState の実装を読んだ。

https://github.com/acdlite/recompose/blob/eaa4dce9b762dc6516341330c49309412efcb58c/src/packages/recompose/withState.js#L19-L20

いろいろやってるけど、やはり setState している。なので先ほどの例でいうと setCounter は、要するにこういう関数として定義される。

function setCounter(updateFn) {
  var newStateValue = updateFn(currentStateValue);
  this.setState({
    'counter': newStateValue
  });
}

なので、 withHandlers の中で setCounter(function(currentValue) { return currentValue + 1 }) のような感じで使われることになり、最終的に setState される。

es2015 の arrow 関数の書き方がいまだにパッと理解できない。

https://github.com/acdlite/recompose/blob/eaa4dce9b762dc6516341330c49309412efcb58c/src/packages/recompose/withState.js#L21-L24

   ({ stateValue }) => ({
     stateValue:
       typeof updateFn === 'function' ? updateFn(stateValue) : updateFn,
   }),

これは、 babel で es5 文法に変換すると、こうなる。

'use strict';

(function (_ref) {
  var stateValue = _ref.stateValue;
  return {
    stateValue: typeof updateFn === 'function' ? updateFn(stateValue) : updateFn
  };
});

ともかく、 withState が何をするのかがなんとなくわかれば、仕事で読み書きする必要があるコードにはなんとか対応できそうなのでよかった。同じ要領で recompose の他の API も読み下せるだろう。


コールドリーディングをしていて他にも馴染みのない表記に出くわした。

https://github.com/acdlite/recompose/blob/eaa4dce9b762dc6516341330c49309412efcb58c/src/packages/recompose/withState.js#L31-L32

これも es2015 からの新しい文法で、オブジェクトのキーに変数を使うとき角括弧で囲うと変数展開してくれる。

qiita.com

JS Bin on jsbin.com

M.ZUIKO DIGITAL 25mm F1.8

買って数枚撮影してすぐに、買って正解だったなと思った。

OM-D E-M1 Mark II を買ってから、 M.ZUIKO DIGITAL 14-42mm F3.5-5.6 Ⅱ R と M.ZUIKO DIGITAL 17mm F2.8 を使ってきたが、 AF-S DX NIKKOR 35mm f/1.8G のような明るい単焦点レンズが欲しいなと思っていた。しかし OM-D E-M1 Mark II は M.ZUIKO DIGITAL ED 12-40mm F2.8 PRO と組み合わせたいという気持ちもあり、他にもたくさん選択肢があるが主に「PRO vs 単焦点」で悩んでいた。

しかし最終的には自分が撮りたいものを一番よく撮れそうなレンズを選ぶことにした。室内で猫を撮るのが主な撮影目的で、次に空と雲。室内の猫ならできるだけ明るい単焦点レンズのほうがよいだろうし、軽くて小さいほうが撮影もしやすいだろうと考えた。

AF-S DX NIKKOR 35mm f/1.8G で撮った写真はとてもすてきで自分の好みに合っていた。 APS-C とマイクロフォーサーズでは同じ F1.8 でも違いがあるとはいえ、焦点距離も同じくらいだし、作例を見ても同じイメージだったので、実際に撮った写真も同じイメージでよかった。