はじめに はじめまして。フロントエンド開発グループに所属している岩釣です。 スタンバイ の月間ユーザー数が1000万人を突破しました!(2023年4月末) 本記事ではそんなスタンバイのフロントエンド開発のコーディングガイドラインを紹介します。 なぜコーディングガイドラインを作るのか コーディングガイドラインを作成して運用するメリットは以下です。 コードの可読性の向上 コーディングガイドラインを規定しチーム全員がコーディングガイドラインに基づいて開発することで、コードの統一性が保たれ、コードの可読性が向上し、保守性が高まります。 チーム内でのコミュニケーションの円滑化 コーディングガイドラインが規定されている場合、チーム内でのコードの書き方についてのコミュニケーションが円滑になります。コードレビューなどでコードの書き方に関して議論する際に、コーディングガイドラインをベースにして議論することで、論点の整理や修正方針の合意が容易になります。 プロジェクトの拡張性の向上 コーディングガイドラインを規定することで、コードの再利用性が高まります。また、コーディングガイドラインに基づいて開発することで、新たな機能の追加や既存機能の変更など、プロジェクトの拡張性を向上させることができます。 開発環境 Nuxt.js 2.15 Pinia 2.0 TypeScript 4.5 ESLint 7.32 Jest Storybook 2022年11月にNuxt3の安定版がリリースされました。現在フロントエンド開発グループでは、Nuxt2からNuxt3へのバージョンアップを鋭意進めています。執筆時点ではまだバージョンアップが完了していないため、Nuxt2を対象とします。 基本方針 基本的には Vue.jsが定義しているスタイルガイド の優先度A ~ 優先度Cまでを守る すでに守れていないファイルに関しては必須とせず、リファクタリングを実施していく 新規作成ファイルにおいては全て守ること 例外的に守らなくて良いものもあるのでそれらは後述する スタンバイでは、既存のファイルに関しては適宜リファクタリングを実施していく方針にして、徐々に改善していきました。 Vue.jsスタイルガイドの中で守らないルール 単一インスタンスのコンポーネント名 違和感がないので「The」というプレフィックスを付けないことを 許容します 。 テンプレート内でのコンポーネント名の形式 ケバブケース(kebab-case)を使うことを 許容しません 。スタンバイではパスカルケース(PascalCase)に統一することで一貫性を重視しました。 ファイル命名規則 .js .tsファイルはケバブケース(kebab-case)を用いる .vueファイルはパスカルケース(PascalCase)を用いる index.vue、error.vue、default.vueのようにNuxt.jsでファイル名が固定で機能が提供されている場合を除く composables/ディレクトリに配置するファイルは以下のルールに従う use-foo-bar.tsのように「use-」から始まること ケバブケース(kebab-case)を用いること 機能が想起できる名前にすること Storybookは{対象のコンポーネント名}.stories.jsにする この辺は好みの問題で、デファクト・スタンダードが無さそうでしたので、チーム内で話し合って決めました。 Atomic Design AtomsとMoleculesの区別をつけずまとめてPartsと呼ぶ OrganismsのコンポーネントはXxxControlのようにサフィックスをControlにする TemplatesはNuxt.jsのlayoutsとみなす PagesはNuxt.jsのpagesとみなす Atomic Design とは、ウェブデザインにおけるデザインシステムの手法の1つで、複雑なUIデザインを簡単に構築するための方法論です。 Atoms(原子)、Molecules(分子)、Organisms(有機体、生命体)、Templates(テンプレート)、Pages(ページ) の5つの構成要素からなります。 Atomic Designの利点は、再利用可能でスケーラブルなUIコンポーネントを作成できることです。これにより、複数のページやアプリケーションで同じUI要素を簡単に再利用できるようになります。 また、小さな部品から大きなコンポーネントを構築することで、保守性が高く柔軟性のあるデザインシステムを実現できます。 一般的に「アトミックデザインを長期的に運用していく上で、Atoms、Molecules、Organismsそれぞれのコンポーネントの定義を明確にすることは重要です。」とされています。 しかし、運用してみるとすぐに気付くのですが、コンポーネントの定義を明確にするのはかなり難しいです。 また、「Molecules」「Organisms」のような舌を噛みそうな単語は馴染めません。 よってスタンバイでは、「Atoms」と「Molecules」の区別をつけずまとめて「Parts」とし、「Organisms」という呼称も使っていません。 Parts(Atoms, Molecules) components/parts/に置く ファイル名は再利用性を考慮して特定のロジックを想起させないことが望ましい 基本的にコンポーネント自身のmarginやpositionやz-indexを持たない(利用側でセットする) 基本的にスマホ/PC両方で利用できる(レスポンシブ) PC専用・スマホ専用のコンポーネントを作成した場合、ディレクトリpc/やsp/を作成してそこに配置する。 Partsは内部でPartsコンポーネント以外の利用不可🙅 ビジネスロジックを書かない @clickイベントや@focusイベントはemitで上位に伝える(Controllerで処理する) ButtonやRadioなどの基底コンポーネントのファイル名はプリフィックスにBaseをつける 状態を持たない(store利用不可🙅) APIコール不可🙅 Storybookを作成しすべてのpropsが操作できるようにする もしデザイナーがCSS/HTMLに熟練している場合、デザイナーにPartsを作成してもらうと分業が捗ります。 そうでなかったとしても、必ずPartsのStorybookを作成するルールにすることで、UIパーツ単体での確認が可能となります。結果、デザイナーとエンジニアの意思疎通がスムーズになります。 Controller(Organisms) components/に置く ファイル名はXxxControl.vueにする Xxxの部分はロジックを想起させることが望ましい Partsからのemitを処理する ビジネスロジックを書く Composition APIで書く できるだけComposableにロジックを切り出して、Composableの単体テストを書く 状態を持ってよい(store利用OK🙆) APIコールOK🙆 Storybookは書かなくてもOK Templates layouts/に置く(Nuxt.jsのlayoutsの機能を提供する) ファイル名はXxxLayouts.vueにする ビジネスロジックを書かない 状態を持たない(store利用不可🙅) APIコール不可🙅 Storybookは書かなくてもOK Pages pages/に置く(Nuxt.jsのpagesの機能を提供する) ビジネスロジックを書く Composition APIで書く Composableにロジックを切り出して、Composableの単体テストを書く 状態を持ってよい(store利用OK🙆) APIコールOK🙆 Storybookは書かなくてもOK Composable Composableとは Vue.jsのComposableとは、Composition APIで書かれた単一の責任を持つ関数やロジックの塊を指します。 スタンバイではcomposables/ディレクトリを作成し、ロジックをuse-logic-name.tsのように別ファイルに切り出しています。 余談ですが、Nuxt3ではcomposables/ディレクトリ内のComposableは自動importされ <script setup> 内で利用できます。 Composableに切り出すことで以下のメリットがあります。 コードの再利用性の向上 コードを再利用できます。コンポーネントで同じロジックを繰り返し書くことがなくなり、コードの保守性や拡張性が向上します。また、開発時間を短縮できます。 テストしやすいコード テストしやすいコードを作成できます。ロジックの単体テストが行いやすくなります。 柔軟なコード構造 ロジックを構造化できます。複雑なロジックを分割でき、コンポーネント内のロジックが複雑になることを防ぐことができます。また、コンポーネントの機能追加や変更に対応しやすくなります。 Composable実装ガイド composables/に置く ファイル名はuse-logic-name.tsのようにuse-から始まること ComposableはAtomic DesignのPagesとOrganismsのみが利用可能 ロジックを再利用可能な状態にしてComposition APIで実装する Composableに切り出したロジックの単体テストを書く 単一の責務の単位でファイルを分割する Composableのサンプルコード 以下は、単純なカウントアップロジックを実装したComposableの例です。 import { ref } from "@nuxtjs/composition-api" ; export default function useCount() { const count = ref(0); const increment = () => { count.value++; } return { count, increment } ; } このComposableは、countとincrementという2つのプロパティを返します。countは現在のカウントを保持するrefオブジェクトであり、incrementはカウントを1つ増やす関数です。 このComposableを使用する場合、以下のように呼び出すことができます。 <template> <div> <p>Count: {{ count }}</p> <button @click="increment">Count Up</button> </div> </template> <script> import { defineComponent } from "@nuxtjs/composition-api"; import useCount from './useCount'; export default defineComponent({ setup() { const { count, increment } = useCount(); return { count, increment }; } }); </script> このComposableの単体テストは以下のように書きます。 import { useCount } from './useCount' ; describe ( 'useCount' , () => { it ( 'increments count' , () => { const { count , increment } = useCount () expect ( count.value ) .toBe ( 0 ); increment (); expect ( count.value ) .toBe ( 1 ); } ); } ); コンポーネント実装ガイド PrettierやESLintではチェックしきれない、コンポーネントの実装に関わるガイドラインです。 HTML内で <template v-if> を多用してHTMLを制御しない 複雑な表示条件ではcomputed()で書くとシンプルになり、且つテストが可能になります。 <template v-if> を組み立てて表示を制御するのは控えましょう。 悪い例🙅 <template> <a> <template v-if="keyword">{{ keyword }}</template> <template v-if="keyword && location" > - </template> <template v-if="location">{{ location }}</template> </a> </template> 良い例🙆 <template> <a>{{ condition }}</a> </template> <script lang="ts"> import { defineComponent } from "@nuxtjs/composition-api"; export default defineComponent({ setup() { const condition = computed(() => `${keyword}${keyword && location ? ' - ' : ''}${location}`) return { condition }; }, }); </script> v-htmlを使用してHTMLを埋め込まない v-htmlを使用すると入力されたHTMLがそのまま出力されるため、XSS攻撃を受ける可能性があります。そのため、基本的にv-htmlは利用しません。 利用する場合は、HTMLとして埋め込む文字列の安全性を担保するためサニタイズします。 悪い例🙅 <template> <p v-html="htmlContent"></p> </template> <script> export default { data() { return { htmlContent: "「<b>営業 未経験</b>」のような条件でも検索できます。" } } } </script> コンポーネントのname属性 IDEやdevtoolsの補助が受けやすいので、Vueコンポーネントのnameプロパティを付与します。 良い例🙆 export default { name: "FooButton" } コンポーネントのemitイベント カスタムイベントをemitさせる際のイベント名は、ケバブケース(kebab-case)にします。 悪い例🙅 this.$emit("clickReset"); 良い例🙆 this.$emit("click-reset"); this.$parentは使用不可 this.$parentを使って親にアクセスすると子と親が密結合してしまうのでNGです。 悪い例🙅 this.$parent.foo = 'bar'; Vue.filterの使用禁止 Vue.filterはVue3で廃止されたので、Vue2のプロジェクトでも出来るだけVue.filterを利用しません。 @clickを付与していい要素 div要素やspan要素に@clickを付与してもキーボード操作時に選択できないため、基本的に@clickはbutton要素とa要素のみに付与します。 CSS実装ガイド コンポーネントの <style> にscopedを付ける Vue.jsはローカルスタイルとグローバルスタイルの混在が可能ですが、ローカルスタイルのみにします。 悪い例🙅 <style> /* グローバルスタイル */ </style> <style scoped> /* ローカルスタイル */ </style> 良い例🙆 <style lang="scss" scoped> .example { color: white; .dark { color: black; } } </style> セレクタのルール ベースとなるスタイル以外では、基本的にはClassセレクタを用いる idセレクタは使用しない(詳細度が必要以上に上がるため) 要素セレクタは使用しない(影響範囲が読みづらくなるため) 詳細度を上げすぎない Classセレクタ3段階程度の詳細度を上限目安とする(ABテスト時に上書きが面倒になるため) セレクタの数を減らすのはパフォーマンスの観点からも有用 セレクタ内で変数は用いない 重複した記述を避けられるなどのメリットはあるが、記述が複雑になるので避ける Class名のルール ケバブケース(kebab-case)を用いる 命名は省略しない(冗長でも誰が見ても分かりやすいようにするため) 悪い例🙅: <div class="bkmrk"> 良い例🙆: <div class="bookmark"> HTML要素に存在する命名を他の要素に使用しない(混乱を招くため) 悪い例🙅: <div class="section"> 状態を示すものは 「is-」「has-」などを付ける 悪い例🙅:selected、error 良い例🙆:is-selected、has-error Partsのルート要素には parts-{ファイル名}のClass名を付ける 悪い例🙅:BaseButtonコンポーネントの場合 <div class="base-button"> 良い例🙆:BaseButtonコンポーネントの場合 <div class="parts-base-button"> 細かい記述ルール 「0」の後の単位は省略する 悪い例🙅:0px 良い例🙆:0 カラーコードが省略可能な時は省略する 悪い例🙅:#ffcc00 良い例🙆:#fc0 カラーコードは小文字にする 悪い例🙅:#FACB12 良い例🙆:#facb12 親から子のスタイルを上書きしない CSSの詳細度利用してスタイルを上書きする /deep/を使わない importantでスタイルを上書きしない plugin等で導入したOSSのUIにスタイルを付ける場合は仕方ないのでOK🙆 ただし将来的にOSSのバージョンアップによって上書きしたスタイルが意味をなさなくなる可能性があるので上書きは推奨しない TypeScript実装ガイド enumは使用せずunionで定義する enumはJavaScriptへのトランスパイルの際に「即時実行関数」に変換されるので、Tree-shakingできません。 そのため、enumは使用せずunionで定義します。 const Color = { RED: "red" , BLUE: "blue" , GREEN: "green" , } as const ; type Color = typeof Color [ keyof typeof Color ] ; ESLintとPrettier ESLint と Prettier はもはや説明するまでもなく、フロントエンド開発においてデファクトスタンダードとなっています。 Vue.jsのESLintプラグイン( eslint-plugin-vue )や、TypeScriptのESLint/Prettier( typescript-eslint )を導入して自動的にコーディング規約をチェックします。 eslintrc.jsは以下を基本として、rulesで細かい調整をしています。eslintrc.jsの調整もチームで話し合って徐々に調整するのが良いでしょう。 また、いちいちプルリクエストで指摘するのは面倒なので、IDEの設定でESLintやPrettierによる整形が自動的にかかるべきです。 スタンバイではVS CodeユーザーとIntelliJ IDEAユーザーがいます。複数のIDEが使われている場合、チーム内でそれぞれのIDEの設定方法を確認すると良いでしょう。 module.exports = { extends : [ "@nuxtjs/eslint-config-typescript" , "plugin:vue/essential" , "eslint:recommended" , "@vue/typescript/recommended" , "@vue/prettier" , "@vue/prettier/@typescript-eslint" , ] , rules: { // 省略 } } 運用方法 プルリクエストのテンプレートに、コーディングガイドラインのリンクを貼る プルリクエストのレビュー観点に、コーディングガイドラインに従っているかを加える コーディングガイドラインが存在していても運用されなかったら意味がありません。 プルリクエストのテンプレートを工夫したり、プルリクエストのレビューのガイドラインも作った方がいいでしょう。 慣れてない時は細かい指摘が沢山でてくる場合もあります。レビュワーの意図が伝わりやすくするため、レビューコメントにはラベルを付けることを推奨しています。 [MUST] :必ず対応してほしい [SHOULD] :時間があれば対応してほしい [IMO] : 自分だったらこうする [NITS] : 小さな指摘点、typoとか [Q] :単なる質問 まとめ 本記事では、スタンバイで運用しているコーディングガイドラインの一部を紹介しました。 すべてを載せきれていませんが、StorybookやJestにも実装ガイドがあります。 本記事を参考に、是非コーディングガイドラインの整備と運用をはじめてみてください。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
はじめに 初めまして、株式会社スタンバイのジョブサーチメインというチームで検索エンジン周りの開発・改善に取り組んでいる金正です。 検索エンジンの改善施策の一環としてクエリオートコンプリーションシステムのリプレイスを行いました。 リプレイスに取り組んだ背景やアーキテクチャ、工夫したことなどをご紹介していきます。 クエリオートコンプリーションとは クエリオートコンプリーションとは、ユーザーの入力から始まるキーワードをサジェストする機能になっています。 以下の例では「エンジニ」という文字列から始まるキーワードをサジェストしています。 以降ではクエリオートコンプリーションのことをQAC(Query Auto Completion)と略します。 スタンバイにおけるQAC スタンバイでは、キーワード検索窓と勤務地検索窓の2つの検索窓が存在しています。 キーワード検索窓では、以下のように検索キーワードをサジェストします。 勤務地検索窓では、住所・駅のみをサジェストします。 サジェストされるキーワードに違いがあるものの、後述するシステム構成自体には特に大きな違いはないので、 後述するシステム構成等の項目ではキーワード検索窓、勤務地検索窓の両方の話をしているんだなと思っておいてください。 QACシステムリプレイスに取り組んだ背景 まずQACシステムのリプレイスに取り組んだ背景からです。 大きく以下のような課題がありました。 構築当時のエンジニアが社内に残っていない QACシステムに関するドキュメントがない QACシステムで利用しているデータやツール・ライブラリのバージョンが構築当初のままになっている QAC以外のデータも同居しているためシステムの分離ができていない 以上のように手が付けられない状態です。 既存QACシステムを改修するよりも、新しく作成しリプレイスする方が工数的にも少ないと判断し取り組み始めました。 現状のQACシステムの課題 現状のQACシステム構成は下図のようになっていました。 サジェストするデータを含んだElasticsearchのコンテナイメージを作成しECSにデプロイすることでQACを実現しています。 このアーキテクチャと前述した背景の詳細課題を列挙すると以下のような課題がありました。 課題点 詳細 1 EMRで動かすスクリプトがメンテされていないので動かない データ元のログのフォーマットやEMR自体の更新がされていないのでEMRを動かそうにも動かせない状態 2 自動化されていない インデックス更新からデプロイまで手動で行う必要がある 3 Elasticsearchのバージョンが2系 QACシステム開発当初のElasticsearchのバージョンのままになっておりセキュリティリスクがあり非常に危険 4 データ更新ができる状態ではないので、最新の検索キーワードに対応できていない 例えば、コロナと入力してもサジェストキーワードが何も表示されない 5 データ元が不明 勤務地サジェストのためには、全国の住所・駅データが必要だが、これも更新されていないので市町村合併や名称変更に追従できていない 6 ログが落ちていない ユーザーの入力キーワードや、そのキーワードに対してどのようなサジェストキーワードを掲出したか等、分析するためのログが落ちていないので改善施策を回すことができない QACのシステムアーキテクチャ 上述した課題を根本的に見直すために全システムの構成を一から見直しました。 まず最初に取り組んだのは、基幹となる検索エンジン部分の技術選定です。 QACシステムを構築する上での要件として以下のような要件がありました。 タイプ 要件 詳細 システム要件 シャーディングは不要だが、レプリケーションが必要 インデックスのデータ量は少なく(大体100MBほど)分散検索は不要だが、負荷分散や耐障害性の向上を目的としたレプリケーションが必要となる システム要件 最小限の構成 検索機能が最小限であり、QACを実現するために必要な機能があること ビジネス要件 ユーザー入力キーワードを日本語かな・漢字、ローマ字のいずれにも対応する 例えば、「とうk」とキーワード入力した際には、「東京」「東急」など「とうk」から始まるキーワードを掲出させる ビジネス要件 複合語にも対応 「エンジニア ふk」とキーワードを入力した際に、「エンジニア 副業」「エンジニア 副業 週一」などのように2つ以上のキーワードも掲出させる必要がある しかし検索エンジンといっても世には様々な検索エンジンが存在しています。 社内では他システムの検索エンジンとしてElasticsearchを利用している実績があります。 ですので、主にElasticsearchとその内部で利用されている検索エンジンライブラリであるLuceneの2つの候補が上がりました。 上記の要件を満たす前提で各検索エンジンのメリット・デメリットをまとめると以下表のようになりました。 メリット デメリット Luceneベース ・最新のLuceneに更新しやすい ・一つの機能(QAC)に特化した検索エンジンを構築できる ・システム構成が簡潔 ・トークンフィルタをカスタマイズ作成・利用可能(ローマ字変換等々) ・スケールアウトしにくい(データ量が増えた際に自前でクラスタリングを構築する必要がある) ・API層を自前で作成する必要がある Elasticsearchベース ・柔軟性がある(設定オプションが豊富でカスタマイズしやすい) ・ドキュメントが豊富 ・スケールアウトしやすい ・トークンフィルタをカスタマイズ作成・利用(ローマ字変換等々) ・最新のLuceneにすぐに対応できない(Elasticsearchのバージョンアップを待つ必要がある) ・前段のAPIサーバとElasticsearchのサーバ2台構成になるのでよりコストがかかる ・QAC以外の機能も備えているので余分な機能も存在する トークンフィルタを駆使すれば、ビジネス要件としてはLuceneベースでもElasticsearchベースでも満たすことが可能だとわかりました。 システム要件の以下2点でLuceneベースの検索エンジンを構築することに決定しました。 より軽量な構成(QACに特化した検索エンジン) Elasticsearchなどの検索エンジン側の対応を待たずに、最新のLuceneへ更新できる 以上の決定を踏まえた最終的なシステムアーキテクチャは以下のようになっています。 INDEXワークフローとQAC-APIの2箇所でLuceneを利用しています。 新システムアーキテクチャのメリット 検索エンジンのコンピューティング(以下、検索)とストレージ(インデックス)を分離したアーキテクチャにしています。 具体的には インデックスをS3のようなストレージに格納 検索時にはそのストレージに格納されたインデックスをダウンロードし、検索を走らせる このようなアーキテクチャにすることによって以下のいくつかのメリットが考えられます。 メリット 詳細 インデクシング・検索のスループット向上 CPU負荷の高いインデクシングが一度だけ行われるので、分離しないものと比較してインデクシングのスループットが向上 インデックスのバージョニングが可能 インデクシングごとに、作成されたインデックスをストレージにアップロードするタイミングでインデックスのバージョニングが可能 またインデックスのバージョンを変更する必要がある場合、インデクシングや検索システムに影響を与えることなく切り替えることが可能 メンテナンス性の向上・スケーラビリティの向上 両方のプロセスを独立してスケールさせることができる 例えば、インデクシングを行うシステムをアップグレードする場合、検索システムには影響は与えない 施策の並列実施 ランキングロジック変更等でインデックスを変更する場合、検索システムに影響を与えることなくインデクシングが完了できるので、さまざまな施策の並行稼働が容易に この検索とインデクシングを分離するアーキテクチャの考え方は、ElasticsearchやAWS OpenSearch(Elasticsearchをフォークしたサービス)でも提案されています。 Elasticsearch公式ブログ AWS OpenSearchドキュメント それぞれのシステムの詳細 次に検索エンジン以外のシステムの詳細を説明します。 ワークフロー自体は airflow で管理するなど、極力管理する工数を削減するために基本的にマネージドなAWSサービスを利用したアーキテクチャにしています。 ETLワークフロー 以下のようなシステム構成になっています。 アーキテクチャ図左端のS3に格納されているリクエストログを元に、下記に挙げる処理をETLワークフローで行います。 記号などの検索に用いるキーワードとしては不適切なキーワードまたは文字の削除 良俗公序に反するNGキーワードの削除 タイポと思われるキーワードのリライト など 実際にユーザーが入力・検索したリクエストログをもとにサジェストキーワードを生成するので、検索窓に適切なキーワードが表示されるように処理を行っています。 INDEXワークフロー 以下のようなシステム構成になっています。 上述したETLを行ったデータから、Luceneのインデックスを作成するワークフローになっています。 ECSの部分は他に、AWS GlueやAWS lambdaと候補があったのですが、コストや利用したいLuceneのバージョンなど様々な制約があったので比較的柔軟に単発バッチを実行できるAWS ECSで構成することにしています。 Luceneのトークンフィルタと自前のトークンフィルタを駆使してのインデックス構築します。 詳細は省きますが、例えば「看護師」というキーワードに対して以下ようにedge-ngram分割・ローマ字変換などの処理をし、インデックスを構築しています。 original_text: 看護師 suggest_text: 看 看護 看護師 k ka kan kang kango kangos kangosh kangoshi より詳細を知りたい方は以下の記事を参考にしてください。 スタンバイアドベントカレンダー2022向けに書いた記事 QAC-API 以下のような使い方をしています。 サーバ起動と同時に、INDEXワークフローで作成したLuceneのインデックスをダウンロードする ユーザーの検索文字列に対して、ローマ字検索するためにLuceneのトークンフィルタなどを活用している ユーザーの入力文字列に対して、Lucene インデックスに検索をかけるだけの非常に薄い構成のAPI インデックスのサイズが平均で100MBほどなので、Elasticsearchなどのようにシャーディングせず、一台のサーバのメモリ上に乗せている 性能 99パーセンタイルで、1.5msほどのレイテンシでサジェストキーワードを返せています。 インデックスのデータ量は旧QACシステムと比べて増加しているのにも関わらず、旧システムの15msと比べると10倍ほど早くなっています。 新旧で利用しているそれぞれの検索エンジンのバージョン以下のようになっています。 旧QACシステム:Elasticsearch 2.3.5 新QACシステム:Lucene 9.3.0 パフォーマンスが上がっている理由としては以下が考えられます。 サーバのCPUコア数が2倍になった 旧システムでは2vCPUに対して、新QACシステムでは4vCPUに増やした サーバにはAWS ECSを利用しているが、利用できるRAMサイズとの関係でCPUコア数を2倍に増やしている経緯がある QACだけで使われるようになたのでシンプルにリクエスト数が減った 旧システムでは、QACの用途以外でも利用されていたコンピューティングリソースをQACの用途だけで使えるようになった Luceneを直接利用することで、レスポンスを返すまでの処理量が減った 旧システムではElasticsearchにカスタムプラグインを組み込んでQACを実現していたので、処理が多数走っていた 運用 上記で説明してきた ETL・INDEXワークフロー、QAC-APIのデプロイ全てを全自動で行っているので、基本的に運用工数はかからないようになっています。 またインデックスの更新頻度は以下表のようになっており、毎日最新のQACサジェストキーワードを表示できるようになりました。 インデックス更新頻度: デイリー インデックスデータ元:過去1日分のログから 評価 新システムへのリプレイス実施を判断するため、「QACの質」と「サービス全体への影響」の2軸で旧システムとの比較評価を行いました。 QACの質 QACシステムの品質は「できるだけユーザー入力の手間を省いて、意図する検索キーワードを表示できていること」と言うことができます。 これを言い換えるとつまり、 「ユーザーの検索窓への入力数が少なく、表示されたQACサジェストキーワードのクリックが増えていること」 上記を構成する指標が以下の2つの指標になっています。 QACサジェストキーワードのクリック率 QACサジェストキーワードのカバレッジ サービス全体への影響 QACシステムの質だけではなく、スタンバイのサービス全体にとってどう影響があるのかも確認する必要があります。 スタンバイでは主に求人クリックと応募を指標として追っているので以下2つの指標を確認しています。 QACサジェストキーワードをクリックした後の求人票クリック率 QACサジェストキーワードをクリックした後の求人票応募クリック率 今後の展望 QACシステムを刷新したことでQACのログも落ちるようになり、分析することが可能になったことで、今後、以下のような改善が考えられるようになりました。 ユーザーからのフィードバックをランキングに組み込む ユーザー属性によって表示するQACサジェストキーワードを変更する インデックスのデータ量を大きくして、より広範囲のユーザー入力に対してQACサジェストできるようにする 最後に 以上、QACシステムリプレイスの概要をご紹介しました。 今回のリプレイスでは、Elasticsearchなどの単一のソフトウェアを使わずに、AWSの様々なクラウドサービスを組み合わせて検索システムを構築しました。 AWSの様々なクラウドサービスを組み合わせて検索システムを構築することによって、以下4点がこの構成をとったメリットかなと考えています。 それぞれ処理したいことに特化したサービスを利用でき、さらに何か機能を追加したい時などのカスタム性が高い 何か不具合があったときに、それぞれのクラウドサービス上で解決するだけで稼働中のシステムに影響ない またどこで何が原因で不具合が発生したのか特定しやすい マネージドサービスのため、運用工数が小さい また個人的には、既存のOSSの検索サーバを利用するのではなくLuceneを直接使うことで、検索エンジンへより深い知識と技術の習得をでき良い経験ができました。 参考文献 Elasticsearch公式ブログ AWS OpenSearchドキュメント airflow公式ドキュメント スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは。DataPlatformグループに所属している小池です。 DataPlatformグループでは、 ●ログ計測と運用を支えるデータ基盤構築(データ基盤整備) ●必要なデータ抽出及びモニタリング環境の整備(データ分析環境整備) ●課題解決におけるデータ活用の支援(データ活用ソリューション) の3つを柱に、データの力でスタンバイの成長を支えています。(縁の下の力持ち) 今回は、2022年4月にリリースした簡易統計モジュールの中で 地域別給与コンテンツにおける統計値が、ユーザーの肌感覚に対して高すぎる という事象に対し、その解決までの取り組みをご紹介致します。 簡易統計モジュールとは スタンバイ において、 求職者に自分が働きたい場所の給与の状況についての 参考情報を提供するコンテンツです。 今回改修対象となった地域別給与コンテンツとは、 エリアごとの給与情報を以下のようにまとめたコンテンツです。 事の発端 2022年、夏も終わりに差し掛かったある日 1つの意見が当コンテンツ開発チームの元に寄せられた。 「給与コンテンツの、富山県の正社員年収が500万近いのは高すぎんじゃね?」 社会人経験も長い、ある富山県出身の社員からだった。 確かに、日本全体での平均給与が436万円(参考:令和元年分 民間給与 実態統計調査)なので、 自社求人データを元に集計している正社員の年収とはいえ、 地方都市にしては高すぎる感は否めない。 (富山県の皆様ごめんなさい) また、各地域で共通の集計方法を用いている以上、 富山県以外の地域でも同様の事象が発生しているとすれば、 スタンバイはユーザーに実態とかけ離れた情報を伝えていることになる。 「このままでは、スタンバイの情報の信頼性が損なわれる」 こうした危機感の下、対策の検討が始まった。 解決までのお話 解決に至るまで議論を重ね次のようなプロセスで進めた。 1. 現状の集計方法の確認 2. 問題箇所の特定 3. 改善案の検討 4. 数値評価 まず、現状の集計(各地域ごとの中央値を算出する)というロジックを確認したが Group Byで地域別に中央値を算出する、という集計ロジックであり特に問題は見当たらなかった。 集計母集団が漏れているのでは等様々なアプローチが試みられたが解決に至らず時間だけが過ぎ去った。 停滞感に支配されかかっていたある日、メンバーの一人がつぶやいた。 「肌感ってなんやねん?」 我々を向き直らせるに十分な一言だった。 この一言をきっかけに、我々は「肌感形成」についての考察から再スタートを切った。 2022年、秋も深まろうという時期のことだった。 より源流へ 〜肌感形成のプロセス考察と最適な集計対象の検討〜 残念ながら我々の周辺には、「肌感」について学術的に精通した人間はいない。 恐らく、この分野の文献を読んでも掛けた時間なりの収穫は難しいだろう。 そこで正攻法でのアプローチを諦め、自身の経験を基に大胆に以下のような仮説を打ち立ててみた。 「日々見聞きする値を基に自身の中に統計ダッシュボードが構築され、これが肌感として定着する。 」 この仮説を基に集計対象として最適な値は何かを検討し、 ここでは 「日々見聞きする値とは最も頻度の高い値、すなわち最頻値」 と考える事とした。 ここまでで、集計の流れが次のように決まった。 1. 各求人の給与の最頻値を求める。 2. その値をもとに、各地域の中央値を集計する。 だが、ここでまた1つ新たな問題にぶつかった。 最頻値を求めるには分布の情報が必要になるが、スタンバイが保有している各求人の給与データは最大値と最小値しかない (そのどちらかのみの場合もある)。 再び、メンバーの苦悩の日々が始まった。 そして解決へ 〜対数正規分布と最尤推定法〜 「そういえば、所得の分布はどのような形状になるのか?」 このような疑問を抱き、厚生労働省が公開している 所得の分布状況 を眺め 次の気づきを得た。 「最頻値が中央値よりだいぶ左だ。あと、低い方に一定限度はあるけど、高い方に明確な限度がない。これ、対数正規分布じゃないか? 」 統計学に関する書籍にも、対数正規分布の事例として年間所得が挙げられている。 更に対数正規分布は正規分布同様に再生性を有するので、前述の厚生労働省が公開している統計の基となる 各企業内の給与分布もまた対数正規分布と考えられる。 これらを基に、以下の仮説を立てた。 「各求人の初任給の分布もまた対数正規分布に近似できる」 ここで再度、各求人の給与の算出方法を確認した所、正規分布を前提とした最頻値の算出方法となっていた。 対数正規分布の最頻値に修正すれば、集計値の改善が見込まれる。問題は、確率密度関数を求める方法だ。 対数正規分布の確率密度関数の式は以下の通り。 すなわち、標本分散(σの2乗)と標本平均μの推定量が出せれば確率密度関数の導出はOKだ。 推定量の導出方法としては、未知数が関数の式の表に出ている事と 微分計算(導関数の導出)が難しくない事から、 最尤推定法 が使えそうだ。 最終的に、各求人の給与の算出式は、以下のようになった。 更に、給与の最大値、最小値のいずれかしか設定されていない求人については集計対象から外す等の 集計対象の調整を加えて、改修を完了した。 結果 各求人の給与の算出法の修正に加え、 ここでは、検証結果の一部を紹介する。 都道府県 改修前中央値 改修後中央値 (参考)政府統計 富山県 4,500,000 2,983,194 2,879,000 福島県 4,750,000 2,965,504 2,965,504 この一部に限らず、全般的に中央値が改修前と比べて現実的な値に近づく事が確認され、 本改修によって、感覚と大きくはずれない統計値を提供出来るようになった。 詳細は是非、実際にスタンバイで働きたいエリアを入力し、 検索後表示される求人一覧ページの最下部に表示される本コンテンツをその目で見ていただきたい。 最後に 本案件の難しさは 感覚的な内容を計算機で扱える形に落とし込む事 に尽きる。 統計は人間の誤った感覚や思い込みを排除して物事を判断するために使う場合もあるが、 今回は、人間の感覚値を正として感覚的な内容を数式で表現し、 計算機で扱える形に落とし込むというアプローチを取った。 今回扱う値については、ある程度年齢を重ねた方の感覚値が正しいように思われたからだ。 また、使えるデータの量が少なかった事や関連の専門知識が不足していた事も解決を困難にした。 この点については「今あるものが最強の武器」と開き直り仮説思考で乗り切った。 今後も、人の感覚に寄り添う統計と補正する統計を上手に使い分け大胆な仮説をもとに 必要であれば他の数学分野の知見、更に社会科学や心理学といった他の学問分野の知見を活用して データに意思を与え、ユーザーにより価値のある情報を提供していきたい。 補足 ここではストーリー中に登場した統計用語について簡単に解説する。 正規分布と対数正規分布 まず正規分布について簡単に説明する。 この分布は自然界や社会現象の多くで現れる確率分布であり、 平均値μが中心となり、標準偏差σが広がりの度合いを表す。 尚、確率密度関数とグラフは以下のようになる。 次に対数正規分布について説明する。 この分布は、正規分布に従うランダムな変数の対数が従う分布で、 特徴は正の値を取ることと歪みがある分布です。 本記事で扱ったように経済学の分野で収入分布を表すのに用いられる他 金融分野や生態学、医療分野などで使用され、例えば、経済成長率や生物種の体サイズ分布などを表すのに使用される。 尚、確率密度関数とグラフは以下のようになる。 最後に、正規分布と対数正規分布の使い分けについて説明する。 世の中には、 平均値からのバイアス(ズレ)が和の形でかかる事象と積の形でかかる事象 が存在し、これらの事象に対して大まかに以下のような使い分けとなる。 ●平均値からのバイアスが、和の形でかかる場合(x=μ+Σε):正規分布 ●平均値からのバイアスが、積の形でかかる場合(x=μ×Πε):対数正規分布 まず、平均値に対してバイアスが和の形でかかる事象の例として 工場のラインで製造された製品寸法について考える。 これは、設計の狙い値(=平均値)に対し加工に伴う誤差が和の形でかかるケースの例であり 製品寸法の分布は正規分布がよくマッチする。 製造の現場では、あるラインがどれだけ決められた規格内で製造出来ているかを 図る指標として工程能力指数((規格上限 - 規格下限) / 6×製品寸法の標準偏差)を 用いるがこれは製品寸法の分布が正規分布であることを前提とした指標です。 もう1つの例として平均値に対してバイアスが積の形でかかる事象を考える。 この場合、生データで分布を描くと正規分布と比べて右に裾野の広い分布となる。 今回扱った年収は、ある基準値に対して前職の実績及び経験年数等に応じて何倍という バイアスがかかっていると考えられる。 (この辺りの知見をお持ちの方がいらっしゃったら、是非教えていただきたいです。) 尚、対数変換を施した値の分布は正規分布になる。 (対数変換によって積は和に変換される為) 最尤推定法 確率密度関数におけるパラメータ推定(今回の場合、正規分布、対数正規分布におけるσ、μの推定)を行う方法の1つとして、 今回使用した最尤推定法がある。 ざっくり説明すると想定した確率密度関数を基に尤度関数を定め、この関数値(尤度)が最大となるときの パラメータ値を最尤推定量として求める、といった流れになる。 詳しくは以下参考文献を参考にしていただきたい。 参考文献 ●統計学入門(東京大学 教養学部 統計学教室 編) ●日本統計学会 公式認定 統計検定1級 対応 統計学(日本統計学会 編) ●仮説思考 BGC流 問題発見・解決の発想法 (内田和成 著) スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは。スタンバイでQuality Assurance(以下QA)を担当している樽井です。 我々QAグループはプロダクトの品質を守り高める存在として、日々の品質業務を改善するためにデータを活用しています。 ここではデータの概要と実際の活用事例をご紹介していきます。 スタンバイのプロダクト開発とQA体制 スタンバイでは、求人検索エンジン「スタンバイ」のWeb・Native Appの求職者向けサービスに加え、求人入稿や広告管理などの企業向けサービスを運営しています。サービスごとに担当開発グループが決まっており、多くのグループは1週間〜2週間単位のアジャイル開発で作業を進めています。 対するQAグループは6名で、複数開発グループからの検証依頼を効率よくこなす必要があります。「特定のメンバーに検証が偏っていないか」「検証の改善ポイントはないか」を中長期的に確認していく上で、データを活用できないかと考えました。 実際にやったこと 検証業務全体のデータを取得 元々は本番障害、検出不具合のみを集計していましたが、検証業務全体を捉えられるようにデータ取得範囲を広げています。 現在取得しているデータ群 ・本番障害 ・検出不具合 ・検証依頼 ・作業工数 ・業務知識 データ元はJira、Zube、Slackなどバラバラです。それらをGoogle スプレッドシートに集約後、データの最終加工をして、Looker Studioへダッシュボードとして出力しています。 図1:Looker Studio仮想データ出力例 月次の数値振り返り会を開催 ダッシュボードは日々、データの急変動がないか、発生した出来事によってどのデータが変わったかを確認しています。 また、毎月頭に先月の数値振り返り会を実施しています。QAメンバー全員が揃い、その月の出来事や忙しさの肌感など定性的な素材と、実際に出た数値の定量的な素材を照らし合わせて、意見交換を行います。 例えば「今月の本番障害が多いのはなぜか」「急に残不具合が増えたのはなぜか」を複数人で話すことにより、「この観点が漏れていた」「この機能の不具合が多く出ていた」「差し込みの案件で優先度が下がった」など、より多くの視点で捉え、実施者が検証中に感じた思いを全員で共有できます。 加えて、過去データを元に「この時期は検証依頼が増えるから細かいものは先に取り掛かっておこう」「この観点で不具合が増えてきているため設計段階でクリアにしておこう」といった直近の作業について話し合いをしたり、データの不明点について議論します。 数値振り返り会内で解決しなかった疑問・調査事項はNext Actionとして担当を決め、不明点が明確になるように努めます。 データ活用事例 事例1:忙しさメーター QA業務の忙しさは、自分たちの感覚を可視化することから始まりました。案件のボリューム&難易度は、ストーリーポイントとして数値化しています。 現在はテスト設計終了時に設計担当者がポイントをつけ、検証終了後にQAメンバー全員でポイントの見直しをする方法を採用しています。 この忙しさメーターにより、「先月は何だか忙しかった」「来週は忙しくなりそう」を可視化できるようになってきました。自分たちの感覚で捉えていたものを他の人にもわかる形にできたことで、共通認識を持てるようになりました。 図2:忙しさメーターの出力例 事例2:業務知識シート QAメンバーは担当する開発グループ・システムが固定化される傾向にあり、知見が属人化している状態でした。 各システムに対して検証に利用するスキルを一覧化し、どのスキルを身につけたのかを毎月チェックしています。スキルは「XXログの取得」「XXシステムの○○操作」「XXシステムのテスト項目作成」の様に分かれています。スキル習得の推移を追うことで、自分自身の成長やQAグループ全体の変化を確認しやすくなりました。「この案件はAさんとBさんが良いね」「この知識が属人化しているから勉強会で共有しよう」など、案件割り振りや教育戦略も立てやすくなりました。 図3:業務スキルシートの出力例 例えば、左図のAさん業務知識を見てみると、4月時点で求人作成サービスの知識は0です。 求人作成サービスに関するスキルは、求人管理のシステム操作がメインになります。この時は複数メンバーが同様の状態であり、特定メンバーに知見が偏っていることがわかりました。 そこでハンズオンの勉強会を実施し、まずは利用頻度の高い「求人作成」スキルを取得してもらいました。このスキルを起点として「求人編集」「会社作成」と次のスキルを身につけていき、12月には求人作成サービスだけでなく、企業向けサービスのスキルも伸ばすことができました。 また、右図のQAチーム業務知識では、個人の業務知識を総合した値を見ています。4月から右肩上がりとなっていたグラフですが、9月は人員入替によりが下がってしまっています。 このような場合、直近スキルを習得したメンバーが教える側に回ることで、新規メンバーのフォローアップと教える側の知識の定着を図ってきました。結果として、以前は5ヶ月間(4〜8月)かけて達した水準に、4ヶ月間(9〜12月)で近づくことができました。 データ活用による変化 検証工数の拡大と不具合検出の増加 データ活用を始めて3年が経過し、ようやく成果らしきものが見えてきました。 3年前に比べ、QAメンバーの全工数におけるテスト工数割合が上昇傾向にあることがわかってきました。これはQA業務の本質である、検証作業に割ける時間が増加していることを示しています。 これまでは、普段担当しないシステムの検証を実施する際、都度インプットを行ったり、知見のあるメンバーが他業務と並行して全面サポートをしたりしていました。現在は各サービスの知見を事前インプットしているため、忙しいタイミングでの説明工数を短縮でき、各メンバーが割り振られた案件に集中できるようになってきています。 工数全体を見ても、一人当たりの稼働時間が増えたわけではないことから、工数を抑えつつ、検証に割く時間を増やせていると言えそうです。 図4:テスト工数割合の出力例 また、検出不具合数も着実に増えています。 こちらは1案件につき平均何件の不具合を検出しているのかを算出したグラフです。不具合内容を精査したところ、過去に知識不足で検出できなかった不具合の増加により、全体の検出数を引き上げている可能性が高いとわかりました。 QAメンバーが持つ知識を意図的に広げていくことで、必要な知識や観点を持って検証にあたることができていると考えられます。 図5:不具合検出率の出力例 今後の野望 新たな課題が発生した時、データを活用することで、客観的な視点で、Next Actionを考えるヒントを得ることができました。これまで目に見えなかった忙しさや頑張り・知識を可視化し、成果の共有ができ始めています。 今はまだQA内部での活用に留まっているデータを、今後はプロダクト関係者、さらには全社的な品質指標へと昇華していきたいと考えています。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
株式会社スタンバイでプロダクトオーナーを務めている上野です。 「スクラム導入したけど、開発が上手く回らない」「これで良いのかわからない」。スクラム開発でお困りの方も多いのではないでしょうか。僕の所属するチームもスクラムが上手 くいかずに苦しんだ時期がありました。 スクラムガイド って守るべきことが書いてあるだけで、運用方法までは書いてないんですよね。少なくともこのあたりは押さえておけば「スクラム赤点」にはならず、軌道に乗るんじゃないかな、という部分をお伝えします。 スクラムとは 僕も当初そうでしたが、スクラムは誤解されやすいフレームです。スクラムガイドがあることからも、「単純にこのガイドラインを守っていれば良いのだな」となりがちな気がしています。 しかし、スクラムは「 現状を素早く正確に把握し、課題を炙り出すフレームワーク 」です。少々乱暴に言えば「目標と現状のギャップを全部見える化し、ギャップを解消することで前進していく」というフレームワークです。導入後も常にチーム全員の努力がない限り成り立ち得ない世界なのです。 そもそも、拠り所となるスクラムガイドに記載されていることといっても、基本的なスクラムイベントやスクラムにおける考え方や価値観に関することくらいです(ただし、心理学や行動学からしっかりと裏付けされた理論)。当然、環境が異なるチームがフレームワークを使うので、唯一の正解となるスクラムの形もありません(「間違った形」は定められそうですが)。 スクラムガイドというと、一般的にスクラムイベントや役割定義に目が行きがちですが(僕自身そうでした)、実はここに記載されている「考え方」がスクラムの成否にかかっていることはあまり知られていないかもしれません。 以前、複数の企業でスクラム導入支援している外部コーチに伺ったところ、スクラムがうまく機能しないケースのうち、最も多い理由が、この 「考え方」の認識が揃っていないこと 、だそうです。 こうした「考え方」部分が重要なのは、スクラム開発がチーム活動であり、チームで助け合うことを前提に作られているフレームワークであるからでしょう。例えば、スプリント内のチケットが早めに完了したメンバーがいたら、他に困っているメンバーの開発サポートに回ったり、POの時間が取れない場合に開発メンバーがチケットの作成まで行なうことなど、ほかにも色々ありえます。 まさにスポーツのように、1つの目標に向かってチームが協力して行う活動は単なる機械的な仕組みだけではなく、「考え方」、いわゆるマインドやスタンスが肝になってきます。 目線のすれ違う開発チーム 当時、僕の所属していたスクラムチームはそれぞれが別の方向を向いている状態でした。 <当時の課題メモ> スプリント内のチケット達成率に対する温度差が生まれてしまっている 個々人で開発の進め方があり、守るべき部分と妥協部分の違いなどが明確にされないまま属人的に進んでしまう レトロ等の振り返りの場でも何を振り返ればよいかわからない状態で、結果的に何も出ない 今思えば、タスクやサブタスクのスプリント内完了の意識を徹底できておらず、開発メンバーがそれぞれの状況を把握できず、それゆえ、協力するような会話ができていないという状態でした。 お互い期待することが異なるため、開発上のストレスが増します。やがて目に見えないストレスはいずれ大きな溝となり、最悪の場合、スクラムチームの崩壊に至っていたでしょう。 幸い、チームの崩壊とまでは至りませんでしたが、パフォーマンス的には”赤点”でした。チーム内から「なんでスクラムやってるんだっけ?」という問いかけが出たほどです。 この状況を打開すべく、チーム外部のコーチ(社内のアジャイルコーチ)の力もお借りし改善に移りました。具体的には、開発の「考え方」を統一させるワークを複数回行いました。週1回程度で1−2時間、1ヶ月程度かけ、主に今から説明する2つの考え方をチームメンバー全員で理解、解釈し、ワーキングアグリーメント化するという活動です。 ワーキングアグリーメントを作って終わり!では意味がないので、チームでの取り決めを日々の、習慣にさせるため、さらに数ヶ月の時間をかけました。 結果、開発チームの雰囲気や生産性は赤点を脱するどころか、組織として次のフェーズに進んだ印象を持てました。スプリント内のチケット進捗率、Slackのコミュニケーション量、普段交わされる話題の数、そして何より、レトロスペクティブによる課題出し、トライの実践、振り返りまでのサイクルが増えたことに驚いています。課題以外にもスプリント内で「良かった取り組み」を付箋化するのですが、この量も当時から比較すると3,4倍違うのではないでしょうか。 スプリントが上手く回るような大それた仕組みを新たに作ったわけではないです。単純に「考え方」を統一しただけです。 この実践を通じて、改めてスクラムメンバーで「考え方」を統一することの重要性を認識しました。「考え方」をいかにチームで合意し、浸透させ、実践するか。これが成功の秘訣であると考えています。チームのカルチャーによってtipsやルールの違いが滲み出るはずで、それがまた面白いところなんだろうなという気がします。前置きが長くなりましたが、以下にて、スクラムで”赤点”にならないための、重要な考え方を2つご紹介します。 1.スクラムの理論 1つ目は、スクラムガイドで「スクラムの理論・スクラムの三本柱」として扱われている考え方です。主に「Inspection(検査)」「Adaptation(適応)」「Transparency(透明化)」の要素から成ります。 少し噛み砕くと ・常に現状に対して自分たちのあるべき状態を常にアップデートし(検査) ・現状とあるべき姿への差分を埋め続ける努力をしていること(適応) ・そもそも正しい状況判断を行えるように、活動を可視化しよう(透明性) というのがスクラムの価値観です。 つまるところ、目標に対して現状はどのような状態なのか、そのギャップを埋めるために何ができるか、が明らかになっていること、ビジネスシーンでよく目にするような「As-is、To-be、To-do」のフレームワークと同じような考え方ですね。 つまり、スプリント内のあらゆる活動において目標が設定され、現状差分に対する仮説を持ち、(可能なら)計測され、公開されるなど、常に振り返れる状態を作り続けないといけません。例えばチケットには「完了基準(Acceptance Criteria)」のような明確なゴールを設ける、スプリント内で消化したストーリーポイントを計測していく、スプリント内で用意できる作業時間を明らかにするなどが該当します。 僕のチームでは、1つ目の「考え方」としてこのスクラムの理論を理解する、というステップから始まりました。 2.スクラムの価値基準 もう1つは、5つのスクラムの価値基準「Commitment(確約)」「Focus(集中)」「Openness(公開)」「Respect(尊敬)」「Courage(勇気)」です。 これだけでも十分なケースもあるかもしれませんが、僕のチームではもう少しアクションに落とし込みたかったので、以下のようにスプリントのワーキングアグリーメントとして設定しました。数が多くても守りづらいので、まずは3つに絞っています。 ポイントは、第三者が見ても客観的に評価できる内容かどうか、です。 ※参考①:miroでのワークショップのアウトプット ※参考②:miroでのワークショップのアウトプット あくまで、上記は僕のチームのカルチャーや課題感から滲み出てきた解釈であり、一例です。他チームで実施すれば別の解釈があると思います。 以上、簡単ではありますが、スクラムの赤点対策に必要な2つの「考え方」をご紹介しました。スクラムで違和感を感じたら、チケットよりも、プロダクトビジョンよりも、「メンバーの目線はあっているか?」を意識してみてください。 赤点を脱した今、個人的には、関わっているメンバー全員が生き生きしているか、楽しんでいるか、を常に意識するようにしています。 まとめ スクラムは「現状を素早く正確に把握し、課題を炙り出すフレームワーク」である 個人技ではなく、チーム活動であることを忘れない スクラムガイドの「スクラム理論」「価値基準」をチームで解釈し、「考え方」を統一することが赤点対策に有効 参考資料 スクラムガイド日本語版(2020年) https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
株式会社スタンバイでプロダクトオーナーを務めている上野です。 「スクラム導入したけど、開発が上手く回らない」「これで良いのかわからない」。スクラム開発でお困りの方も多いのではないでしょうか。僕の所属するチームもスクラムが上手 くいかずに苦しんだ時期がありました。 スクラムガイド って守るべきことが書いてあるだけで、運用方法までは書いてないんですよね。少なくともこのあたりは押さえておけば「スクラム赤点」にはならず、軌道に乗るんじゃないかな、という部分をお伝えします。 スクラムとは 僕も当初そうでしたが、スクラムは誤解されやすいフレームです。スクラムガイドがあることからも、「単純にこのガイドラインを守っていれば良いのだな」となりがちな気がしています。 しかし、スクラムは「 現状を素早く正確に把握し、課題を炙り出すフレームワーク 」です。少々乱暴に言えば「目標と現状のギャップを全部見える化し、ギャップを解消することで前進していく」というフレームワークです。導入後も常にチーム全員の努力がない限り成り立ち得ない世界なのです。 そもそも、拠り所となるスクラムガイドに記載されていることといっても、基本的なスクラムイベントやスクラムにおける考え方や価値観に関することくらいです(ただし、心理学や行動学からしっかりと裏付けされた理論)。当然、環境が異なるチームがフレームワークを使うので、唯一の正解となるスクラムの形もありません(「間違った形」は定められそうですが)。 スクラムガイドというと、一般的にスクラムイベントや役割定義に目が行きがちですが(僕自身そうでした)、実はここに記載されている「考え方」がスクラムの成否にかかっていることはあまり知られていないかもしれません。 以前、複数の企業でスクラム導入支援している外部コーチに伺ったところ、スクラムがうまく機能しないケースのうち、最も多い理由が、この 「考え方」の認識が揃っていないこと 、だそうです。 こうした「考え方」部分が重要なのは、スクラム開発がチーム活動であり、チームで助け合うことを前提に作られているフレームワークであるからでしょう。例えば、スプリント内のチケットが早めに完了したメンバーがいたら、他に困っているメンバーの開発サポートに回ったり、POの時間が取れない場合に開発メンバーがチケットの作成まで行なうことなど、ほかにも色々ありえます。 まさにスポーツのように、1つの目標に向かってチームが協力して行う活動は単なる機械的な仕組みだけではなく、「考え方」、いわゆるマインドやスタンスが肝になってきます。 目線のすれ違う開発チーム 当時、僕の所属していたスクラムチームはそれぞれが別の方向を向いている状態でした。 <当時の課題メモ> スプリント内のチケット達成率に対する温度差が生まれてしまっている 個々人で開発の進め方があり、守るべき部分と妥協部分の違いなどが明確にされないまま属人的に進んでしまう レトロ等の振り返りの場でも何を振り返ればよいかわからない状態で、結果的に何も出ない 今思えば、タスクやサブタスクのスプリント内完了の意識を徹底できておらず、開発メンバーがそれぞれの状況を把握できず、それゆえ、協力するような会話ができていないという状態でした。 お互い期待することが異なるため、開発上のストレスが増します。やがて目に見えないストレスはいずれ大きな溝となり、最悪の場合、スクラムチームの崩壊に至っていたでしょう。 幸い、チームの崩壊とまでは至りませんでしたが、パフォーマンス的には”赤点”でした。チーム内から「なんでスクラムやってるんだっけ?」という問いかけが出たほどです。 この状況を打開すべく、チーム外部のコーチ(社内のアジャイルコーチ)の力もお借りし改善に移りました。具体的には、開発の「考え方」を統一させるワークを複数回行いました。週1回程度で1−2時間、1ヶ月程度かけ、主に今から説明する2つの考え方をチームメンバー全員で理解、解釈し、ワーキングアグリーメント化するという活動です。 ワーキングアグリーメントを作って終わり!では意味がないので、チームでの取り決めを日々の、習慣にさせるため、さらに数ヶ月の時間をかけました。 結果、開発チームの雰囲気や生産性は赤点を脱するどころか、組織として次のフェーズに進んだ印象を持てました。スプリント内のチケット進捗率、Slackのコミュニケーション量、普段交わされる話題の数、そして何より、レトロスペクティブによる課題出し、トライの実践、振り返りまでのサイクルが増えたことに驚いています。課題以外にもスプリント内で「良かった取り組み」を付箋化するのですが、この量も当時から比較すると3,4倍違うのではないでしょうか。 スプリントが上手く回るような大それた仕組みを新たに作ったわけではないです。単純に「考え方」を統一しただけです。 この実践を通じて、改めてスクラムメンバーで「考え方」を統一することの重要性を認識しました。「考え方」をいかにチームで合意し、浸透させ、実践するか。これが成功の秘訣であると考えています。チームのカルチャーによってtipsやルールの違いが滲み出るはずで、それがまた面白いところなんだろうなという気がします。前置きが長くなりましたが、以下にて、スクラムで”赤点”にならないための、重要な考え方を2つご紹介します。 1.スクラムの理論 1つ目は、スクラムガイドで「スクラムの理論・スクラムの三本柱」として扱われている考え方です。主に「Inspection(検査)」「Adaptation(適応)」「Transparency(透明化)」の要素から成ります。 少し噛み砕くと ・常に現状に対して自分たちのあるべき状態を常にアップデートし(検査) ・現状とあるべき姿への差分を埋め続ける努力をしていること(適応) ・そもそも正しい状況判断を行えるように、活動を可視化しよう(透明性) というのがスクラムの価値観です。 つまるところ、目標に対して現状はどのような状態なのか、そのギャップを埋めるために何ができるか、が明らかになっていること、ビジネスシーンでよく目にするような「As-is、To-be、To-do」のフレームワークと同じような考え方ですね。 つまり、スプリント内のあらゆる活動において目標が設定され、現状差分に対する仮説を持ち、(可能なら)計測され、公開されるなど、常に振り返れる状態を作り続けないといけません。例えばチケットには「完了基準(Acceptance Criteria)」のような明確なゴールを設ける、スプリント内で消化したストーリーポイントを計測していく、スプリント内で用意できる作業時間を明らかにするなどが該当します。 僕のチームでは、1つ目の「考え方」としてこのスクラムの理論を理解する、というステップから始まりました。 2.スクラムの価値基準 もう1つは、5つのスクラムの価値基準「Commitment(確約)」「Focus(集中)」「Openness(公開)」「Respect(尊敬)」「Courage(勇気)」です。 これだけでも十分なケースもあるかもしれませんが、僕のチームではもう少しアクションに落とし込みたかったので、以下のようにスプリントのワーキングアグリーメントとして設定しました。数が多くても守りづらいので、まずは3つに絞っています。 ポイントは、第三者が見ても客観的に評価できる内容かどうか、です。 ※参考①:miroでのワークショップのアウトプット ※参考②:miroでのワークショップのアウトプット あくまで、上記は僕のチームのカルチャーや課題感から滲み出てきた解釈であり、一例です。他チームで実施すれば別の解釈があると思います。 以上、簡単ではありますが、スクラムの赤点対策に必要な2つの「考え方」をご紹介しました。スクラムで違和感を感じたら、チケットよりも、プロダクトビジョンよりも、「メンバーの目線はあっているか?」を意識してみてください。 赤点を脱した今、個人的には、関わっているメンバー全員が生き生きしているか、楽しんでいるか、を常に意識するようにしています。 まとめ スクラムは「現状を素早く正確に把握し、課題を炙り出すフレームワーク」である 個人技ではなく、チーム活動であることを忘れない スクラムガイドの「スクラム理論」「価値基準」をチームで解釈し、「考え方」を統一することが赤点対策に有効 参考資料 スクラムガイド日本語版(2020年) https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
はじめに こんにちは、Tech blog運営担当の青山です。 スタンバイには、プロダクトの行動指針「START」に基づいて、素晴らしい成果を創出したメンバーを表彰する「START賞」という表彰制度(月次表彰)があります。 また、半期(上期・下期)ごとに、プロダクト部門所属のメンバーの中から1名に「ベストプレイヤー賞(プロダクト部門)」が贈られます。 (行動指針「START」の詳細は、 「スタンバイのプロダクト本部の行動指針「START」と「Engineering Belt」とは?」 にてご確認ください) 本記事では、入社1年未満にもかかわらず、「6月度START賞 MVP」「10月度START賞 準MVP」「FY22上期 ベストプレーヤー賞(プロダクト部門)」を受賞し、プランナーとして大活躍中の城本さんに、高評価を獲得したプロジェクト「Apply URL プロジェクト」について、インタビューをしていきたいと思います! -早速ですが、「Apply URL プロジェクト」について、概要を教えてください。 城本: 一言で伝えると「ユーザーの応募体験向上」のためのプロジェクトです。 以前、ユーザーインタビューを実施した際に、求職者様より「スタンバイの求人詳細ページの「応募ボタン」を押すと、掲載元の求人詳細ページに遷移してしまう。すぐに応募したいと思っている分、応募フォーム以外のページに遷移するのは分かりにくいし、不便」などのお声をいただいていました。 そのお声をもとに、求人詳細ページの「応募ボタン」を押した後は、掲載元の応募フォームに直接遷移させることを「ユーザーの応募体験向上」のための初手だと考えました。 また、クライアント企業(求人掲載元の企業)様にとっても、求人検索の利便性を高めることは、「企業様と求職者様とのマッチング」の機会を増やすことに直結します。比較的シンプルなステップで企業様が導入できる「Apply URL」は、まさにファーストステップとしてうってつけでした。 -ありがとうございます。求職者様の応募体験を向上させることで、「スタンバイで求人を検索し、応募に進む機会」をより多く提供していきたいですね。次に、多くの賞を受賞することになった「Apply URL プロジェクト」における城本さんの具体的役割を教えていただきたいです。 城本: プランナーの基本的な役割は、施策のROIの見立て、要求・要件の整理、スケジュール管理などです。それに加え、スタンバイには様々なバックグラウンドを持ったプランナーが集まっており、エンジニア出身、デザイナー出身、コンサル出身など各人のスキルによって得意分野が異なり、プロジェクトの内容によっても、プランナーとしての役割は多少変化します。 「Apply URL プロジェクト」においては、機能をリリースするだけでなく企業様に導入いただくことまでを視野にいれる必要があるので、導入率向上を含めた戦略の立案も私の大事な役割でした。あまり具体的に表現するのが難しいのですが、プランナーの役割は、「プロジェクト成功にむけて、開発実装以外はなんでもする人」と言えばよいかもしれません。 -「開発実装以外なんでも」・・・聞いただけでも大変そうですが、その分やりがいがありますね。「Apply URL プロジェクト」のステークホルダーについても教えてください。 城本: 開発部門内だと、自グループ以外の4つのグループが関わっていました。また、それ以外では法務、セールス、CSのグループにご協力いただきました。 -自グループにとどまらず、他に7つのグループと関わりながらプロジェクトを推進されたのですね。城本さんが表彰された大きな理由の1つは、「“Apply URL プロジェクト”をスムーズに進められた」ことだと伺いました。これだけ多くのグループが関わったプロジェクトを成功に導くための「プロジェクト推進術」を知りたいです。 城本: そうですね、まず前提として、過去の経験と比較しても、スタンバイのエンジニアは開発のスピードが速く、プロジェクトにおける提供価値の擦り合わせが完了すれば、要求を基に要件・仕様の提案などを積極的に行って自走していく優秀な方が多いと感じます。また、私のような非エンジニア人材に対しての技術的な説明も分かりやすいので、プランナー側でも設計意図を理解しながら実装を進めることができます。 技術・意識ともにプロフェッショナルな方々と共に進められたことが、プロジェクト成功の大きな要因といえますが、それ以外で「プロジェクト推進」において上手くいった要因を挙げるとするならば、振り返ると3つほどあると思います。 1.40%の完成度でもいい。たたき台を作成する 私には、数値分析やデザインなどの専門性はありません。完成度の高い資料の作成は難しいですが、たたき台やワイヤーフレーム、分析要件など40%の完成度でもいいので、まずはとにかく形にすることを心がけています。 それを基にお願いしたいことを説明すれば議論がしやすくなり、意思伝達のスピードが上がります。自分にないスキルは積極的にスキルのある人や専門グループを頼らせてもらい、40%のたたき台を80%以上のクオリティに仕上げるのです。 また、その40%のたたき台の中にも「ぶれない箇所・要素」はあるので、他グループへの相談を併行しながら、要件整理を進めていきます。これは、コミュニケーションの効率化とプロジェクトの推進スピードの向上に多少ながら寄与したと考えています。 2. なぜやりたいのかを論理的に全力で説明する 「Apply URL プロジェクト」に関しては、当初ステークホルダー全員がポジティブな想いを持っている状態とは言えませんでした。なぜなら、課題に対して多くの解決策がある中で、それがベストな打ち手といえる認識を統一できていなかったからです。 そこで、「仮説の数値を基にした効果」や「他の手段と比較した結果」などを踏まえ「なぜやりたいのか」を論理的に説明することに力を割きました。さらには、このプロジェクトは、「中長期的な視野で、将来につながる」プロジェクトであることを理解してもらうことも欠かせませんでした。 また「Apply URLが今選択しうるベストな打ち手である」というストーリーを考えることは、私にもできることですが、「誰から伝えるのか」は、ストーリーの納得感を高めるためにも、とても重要です。そのため、最終的にはマネージャーやCOOにも協力を仰いで関係者との認識合わせを行いました。 批判や反対意見は、裏を返せばスタンバイを成長させたいという想いがあるからこそ出てくる、有り難い言葉です。そういった意見からプロジェクトのロードマップや機能仕様を適切に変更することができた経験もあります。目線のずれている箇所を洗い出し、すり合わせることに全力で向き合うことで、プロジェクトメンバーの一体感を高めることができたのではないでしょうか。 3.QCDSに沿って、プロジェクトを管理する 最後に基本的なことですが、プロジェクト管理においては、「QCDS」(品質:Quality、予算:Cost、納品:Delivery、スコープ:Scope)という管理指標があります。 プロジェクトマネジメントをされている方にとっては当たり前のことだと思いますが、私は基本に忠実に「QCDS管理」を大切にしています。 また、スタンバイのフェーズではMVP(Minimum Viable Product)開発も意識する機会が多いです。MVPとは、一般的に「顧客にとって、価値が提供できる最小単位」として要件を定義することですが、リリースに必要なスコープを絞ることでデリバリーを早め、機能追加・改善のサイクルを早くまわすことができます。 実装が進むにつれて工数の見積もり精度も高くなるため、「リリースするに足る最小単位」の基準を決めておくことで、リリース日の延期が必要かどうかを早めに判断できます。 さらに、開発を進めていく中では、「あれもやりたい」「これもやりたい」ということが、常に出てきます。このような場面においても、プロジェクトのMVPやQCDS基準を基に議論することで、「デリバリー期日に間に合う範囲で、このWANT要件を追加できないか」などのスコープ調整の提案もしやすくなります。 プロジェクトが進行する間、共通の判断軸を基に議論することで、結果として手戻りを少なくし、プロジェクトの進行スピードを一定に保つことができます。 -ありがとうございます。言うは易し行うは難しですが、3点とも城本さんは、ごく自然に行動されているなと感じました。上記のような「プロジェクト推進術」をお持ちの城本さんでも「Apply URL プロジェクト」において、「スムーズに進まないこと」や「スムーズに進まなさそうなリスク」はなかったのでしょうか? 城本: 機能をリリースすることに関しては、あまりスムーズにいかないという場面はなかったと思います。ですが、「Apply URL」は、ただ作っただけでは意味がなく、企業様に導入いただかないと求職者様に体験していただけません。導入する・しないはもちろん企業様が判断されるので、導入が進まずに作ったものが無意味になる事態は、このプロジェクトにおいて私の力では調整がしにくいリスク要素だと感じていました。 そのため、セールス部門への相談やヒアリングは丁寧に行いました。機能に対して好意的な印象を持っている企業様や、導入可能性のある企業様とのコミュニケーションを代替していただき、リリースのタイミングについても、他の施策との抱きあわせで、企業様が「このタイミングならまとめて対応できる・導入しやすい」という時期にデリバリー時期をあわせることをプロジェクトゴールに含めました。良いプロダクト作りは開発だけの視点では、成し得ません。開発・企業・ユーザーにとって「三方が良い状態」を目指すことができたことも、プロジェクト成功に起因していると思います。 -ありがとうございます。関係者に最大限に配慮する想いと行動が、プロジェクトをスムーズに進め、成功に導いているんだと感じました。 私自身も城本さんがリードするプロジェクトに関わっていますが、「スムーズ」と感じる裏には、城本さんのきめ細かい配慮が隠れていたことに気づきました。貴重なお話をありがとうございました! スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは、スタンバイのTech blogの運営担当の青山です。 あっという間に年末年始休みも終わり、気持ち新たに仕事初めを迎えられましたでしょうか。 みなさま、本年もスタンバイをよろしくお願いいたします。 さて、2023年の1回目のTech Blogの記事ということで、今回は、スタンバイの技術的軌跡をまとめた弊社CTO明石の 「2022年スタンバイアドベントカレンダー」の12月25日の記事 を、こちらでもご紹介します。 はじめに (※本記事は、「Stanby Advent Calendar2022」の再編集記事です。また、執筆時2023年1月時点の情報です。) タイトルにあるように、なぜ「2+1年」なのかは、 インタビュー記事(事業成功にフォーカスした、エンジニアのプロ集団を作っていきたい) を参照するのが良いのですが、読むのが面倒な方のために簡単に説明すると、スタンバイへJoinする1年前の2019年12月末に代表の南と邂逅し、ヤフーの明石としてスタンバイの検索精度向上の取り組みを始めたのが2020年1月、スタンバイにJoinしたのが、2021年1月ですので、2+1年の軌跡になります。 技術負債との闘い スタンバイでのエンジニアリングの取り組みを例えると技術負債との闘いのひとことで締めくくれるかと思います。 株式会社スタンバイは、2019年12月にZホールディングス株式会社とビジョナル株式会社(当時、株式会社ビズリーチ)のJoint Ventureとして設立された企業ですが、いわゆるスタンバイサービスは、ビズリーチの一事業を継承したものであるため既に4-5年の歳月を積み重ねてきたいわゆる年代物のサービスです。 4-5年間サービスを継続し続けるというのは、それはそれで一つの尊敬できるものであり、スタートアップの中でも数少ない生き残りともいえ、その実績と成果があってのJoint Ventureであるのも頷けるのですが、4-5年の歴史というのは、それなりにしょっぱい技術負債を樽の中に詰め込んで居るものなのです。 検索エンジン 最初に手をつけたのが、このサービスの心臓であり、最大の差別化ポイントの検索エンジンでした。ちなみにスタンバイの検索エンジンには、Organic(無料掲載求人)とAd(有料掲載求人)の2種類の検索エンジンがあり、GoogleやヤフーなどのWeb検索と同様に有料掲載求人については、Organicの結果の上位、中位、下位の固定枠に表示されます。 インタビュー記事(事業成功にフォーカスした、エンジニアのプロ集団を作っていきたい) で語ったように、ヤフーvsスタンバイでOrganic検索のABテストコンペを実施し、その結果、ヤフーの方式が採用されることになりました。いまだから話せますが、当時のスタンバイでは、この結果はわかっていたことでしたが、あなたたちは検索エンジンのことを理解していないと外の人間がいきなりやってきて話しても、だれも納得しないのでこのイベントを実施し、ヤフーとの協業という形をスムーズに進めることができたかと思います。 結果として、 Organic検索エンジンにはYahoo!ABYSSという検索プラットフォーム Ad検索エンジンにはElasticsearch を採用。 これは、Yahoo!ABYSSを利用し、ヤフーエンジニアとの協業を進めることにより、ヤフーの持つ検索Knowledgeをスタンバイに吸収し、スタンバイの検索技術の底上げを図ることを目的とし、その一方で、Yahoo!ABYSSで培った知識を自社検索エンジンに取り込むことにより、スタンバイ内での検索エンジンの知識や運用経験を養うためにこの2エンジン体制をとることにしました。 ヤフーの検索エンジンをお借りすることは非常にメリットの多いことなのですが、これは、諸刃の剣であり、ヤフー内の方針でこのエンジンの提供をいつ止められてもスタンバイが死なないようにこの体制にしました。これは、ヤフーを非難する訳ではなく、ソフトウェアにはライフサイクルもあり、ヤフーだけでなく、AWSなどクラウドそして、僕自身ヤフーのCTOなどを経験して、実際にこのような外部提供機能を様々な事情により断念した記憶があるからです。あと、そのほうがエンジニアのモチベーションも維持できますからね笑 Organic検索 Organic検索エンジンの一番の負債は、スタンバイサービス開発初期に作られた学習モデルが更新されずに改善されていなかったことですが、この取り組みによって大きく前進、さらに定期的な新モデルでのテストも実施されるようになり、改善サイクルがまわるようになりました。 Stanby Tech Blog スタンバイの検索の仕組み にてご確認ください。 Ad検索 一方、Ad検索エンジンについては、エンジンの切り替えをしなかったのと、多くの意味不明なアルゴリズムが随所に蓄積されていたためこんなに簡単ではありませんでした。 最初の取り組みとして、以下を実施しました。 1.新モデル適応と GSP 導入 2.Second Phase Ranking の導入 3.アルゴリズムとモデルの統一 Stanby Tech Blog スタンバイの広告表示におけるロジックについて あたりを参考にされると良いかと思います。 1.新モデル適応とGPS導入 GSPっぽいものが入っていたが、GSPは、Auction理論を応用して、価格を上限適正値に引き上げていくものなのですが、GSPっぽいものは、価格を中央適正値に寄せるようなもので、これだと価格適正化が行われそうもありませんでした。モデルは、Organicと同様で、サービス開発初期に作成されたものでした笑 2.Second Phase Rankingの導入 Adにおける、Second Pahse Rankingの重要性については、期待収益額順にRe-Rankingして掲載順を決定するのですが 例: 期待収益額 = 入札額 × 予測CTR このときに予測CTRがものすごく低く、入札額が高い不適切な広告が上位に来てしまうことが発生します。これを避けるために、First Phaseで適合性の高い広告順(上述では、予測CTR順)でRankingした上で、上位n本をSecond Phaseに渡し、期待収益順にRe-Rankingすることにより、適切な広告間でのAuctionを実現する必要があります。ElasticsearchのRe-Rank Plug-inである、 Learning to Rank がそれにあたるのですが、パフォーマンスが出ない、Yahoo!ABYSSのKnowledgeを取り入れるためには、拡張性に欠けるということで独自開発する必要がありました。 3.アルゴリズムとモデルの統一 なんとなくそれぞれの背景は想像ついたのですが、検索条件とか出面によってアルゴリズムが異なる。ここでは、詳細には触れたくないほどの負債でした。 取り組んだ結果 4-5年の歴史と 運用型広告 とはすごいもので、新モデル適応と[GSP]の取り組みだけでは、旧アルゴリズムには勝てませんでした。Second Phase Ranking の導入を並行して進めたことと、社内のMLチームの努力もあって、今年の4月に文句ない状態で1〜3を達成することができました。 結果として、4月〜12月までで求人企業様のROIを向上しつつ、広告収益性は2倍近く向上できるような体制になってきました。 先述のインタビュー記事では、ここまでを1〜3までを1年でと考えていたので結果2年掛かってしまいました。 求人データ取り込み 皆さんご承知かと思いますが、スタンバイは、求人検索エンジンです。求人検索エンジンで重要なものをふたつあげてと言われれば、先ほど、紹介した、検索エンジンとその検索エンジンのコンテンツである求人票です。 スタンバイの求人票は、3つの方法で取り込まれています。 1.データフィード 2.クロール 3.CMSからの入稿 このうち、1.と2.については、ジョブデータコアグループの開発する仕組みで実現されていますが、この仕組みでは、求人票の収集、分析、蓄積、管理、(分類)などが責務があり、なによりフレッシュネスをいかに担保するかが大きな課題です。Daily約2000万前後の求人の取り込みと、規約確認、同一求人などの分析や判別、そして、管理、求人票判定器によって判定された結果の反映などが実施され、結果としてスタンバイに掲載される求人票は約1200万(まとめ求人を考慮すると約800万)程度を処理する仕組みとなっています。 この求人取り組みも大きな課題を抱えており、2021年11月頃から大幅なリプレースを実施しました。 Stanby Tech Blog スタンバイの求人情報取込の仕組みを作り直した話 〜序章〜 Stanby Tech Blog 求人取り込み周りのリプレイスについて Advent Calender 2022 クローラー(求人取り込みシステム)のリアーキ対応について この件については、Tech Blogにしっかり記述されているので特筆しなくても良さそうですね。 結果として、法律対応や求人票拡充のためのカラムの追加など柔軟に対応でき始めています。 最近の取り組みとしては、クロールデータのフレッシュネスを担保するための終了求人チェックを如何に効率よく行うかと言う課題に取り組んでいるところです。 Stanby-API Stanby Tech Blog プロダクト開発体制のこれまでとこれから に紹介されているとおり、2021年4月の組織変更で、技術ドメインに基づいたチーム体制に変更しているのですが、その弊害として、複数チームがメンテナーとなっているのが、このStanby-APIといサービスです。 スタンバイは、 マイクロサービスアーキテクチャ を採用しているのですが、マイクロサービスって進めていくと、マイクロサービス群をいい感じにマッシュアップしてくれたり、ドメインの責務から外れるようなアルゴリズムなどもすべて集約されるいわゆるかゆいところに手が届く的なサービスができあがるのですが、いわゆるこれがそれにあたります笑 結果として、何か機能改修が必要な際には必ずメンテナンスを余儀なくされ、ほとんどのチームがメンテナーとなっているので、開発効率を著しく下げています。 詳細については、このプロジェクトが終わった頃、担当エンジニアが書くかもしれないので割愛しますが、モジュラモノリス化により、この課題に取り組んでいます。 Geo-API 求人サービスとして、Geographic情報の把握と理解というのは、重要な課題の一つなのですが、全スタンバイエンジニアがヤバいなーと思いながら見て見ぬふりをし続けたのがこのGeo-API。 Geo-APIにはいくつかの機能があり、 ●基本機能 ○住所のNormalization (住所文字列の正規化) ○Geo-Coder (住所から緯度経度情報への変換) ○Reverse Geo-Coder (緯度経度情報から住所への変換) ●その他 ○駅などの地点情報の判定 ○地点情報の住所の特定 みんなが言うには、 誰も仕様を理解していない どの機能がどこで使われているのかも分からない コードめちゃくちゃなので触りたくない と、カオスですね笑 このパンドラの箱をとうとうこじ開けました。Phase-1は、春には目にすることができそうです。 QAC(Query-Auto-Completion) Advent Calender 2022 日本語クエリオートコンプリーションの実現 でも紹介された、いわゆる検索サジェスト機能です。 これも、開発当初から一切メンテされていない、効果測定もしたことないからよくわからない、という誰も触れなくなったモジュール。 Lightな機能だからこそElasticsearchやSolrの機能を使わずに、Luceneで開発して、検索技術の理解を深めようというのが今のスタンバイのエンジニアらしい取り組みですね。 時がたち社内のエンジニアのレベルが維持できなかったり、環境が変わったときに負債化するので、もっと、汎用的な仕組みにすべきでは?という意見が聞こえてきそうですが、今は検索技術の理解のほうが重要だと思ってます。 これは、そろそろ、第一弾のABテストが始まるのかな。 他にもご紹介したことはたくさんありますが、今回はこのあたりにしたいと思います。この記事を書きながらも、新規リリース目白押しで、楽しみな年になりそうです。 スタンバイは、まだまだ、これからです! 最後に 2023年も新規リリースが目白押しとのことです。Tech Blogでも新規リリースにフォーカスした記事も投稿していきたいなと思っています。リリースもTech Blogの記事も楽しみにしていてくださいね! スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
はじめに ジョブデータコアグループに所属している池田です。 ジョブデータコアグループでは、求人情報の取り込み、求人情報の管理、検索エンジンまでのインデックスを行っております。 我々のチームでは2020年11月からスタンバイのクローリングシステムをリアーキテクト・リプレイスしたのですが、 今回はその時の一部のプロダクトについて課題と実際に2年間運用してどうだったのか振り返りを書いていきます。 課題と背景 課題背景は過去のブログ「 スタンバイの求人情報取込の仕組みを作り直した話 〜序章〜 」でも記載しましたので詳細は割愛しますが 当時求人取り込みの現場で以下のような問題が発生しておりました。 求人取込から広告掲載まで最大6-7時間かかる 求人のフィールドを変更するのに1ヶ月かかった 求人情報の論理的な破損が発生後、復旧までに1ヶ月かかった 大きな問題として 「求人・広告データが1箇所で保管されていることで、データの柔軟性、拡張性に問題が発生している」 という技術的負債があったのでリアーキテクトを行っていったのですが、 その中でレガシーコード化が進んでいるものがありました。 それが es書き込みバッチ というシステムです。 ※1 レガシーコードとは (理由はなんであれ) 修正、拡張、作業が非常に難しいコードのことを指しています。 (「レガシーコードからの脱却」より) es書き込みバッチについて es書き込みバッチ は クローリングした求人から、求人の情報を抽出し、正規化して、検索エンジンへインデクシングするという責務を担っております。 図の es書き込みバッチ となっているシステムになります。 2015年から開発されて、 Scala と Apache Spark (オープンソースの分散処理システム)で作られておりました。 システムの処理の流れとしてクローリングデータ(文字列のjsonのデータ)を受け取り、以下のパイプライン処理を行います。 クローリングデータ(jsonデータ)から求人のフィールドの抽出 フォーマットチェック 求人情報の正規化、 検索エンジンへの書き込み しかし処理の過程の型が Option[Map[String, Option[Any]]] という型で どのような変更が行われ、最終的にどの項目が書き込まれているのか、ということがコードからすぐには理解できない状態でした。 各パイプラインの返り値の型 // jsonから求人データの抽出した後の型 Map[ String , Option[Any]] // フォーマットチェック後の型 Option[Map[ String , Option[Any]]] // 求人情報の正規化後の型 Option[Map[ String , Option[Any]]] Optionは値がその名の通り値がオプショナルである型で、値があれば値を返し、なければ値を返しません。 Mapは、「キー」と対応する「値」の2つの要素をペアにして格納するデータ構造です。 Scalaの世界のAny型はもっとも汎用的な型になり、どの型の値も入れることができます。 簡単な例ですが、 Map[String, Option[Any]] は以下のようなデータを入れることができます。 val sample = Map[ String , Option[Any]]( "jobTitle" -> Option( "バックエンドエンジニア" ), "jobContent" -> Option( "スタンバイのバックエンド開発業務" ), "jobType" -> Option( Array [ String ]( "正社員" )), "siteName" -> Option( "スタンバイ" ), "salary" -> Option( Map( "displayString" -> None ) ) ) アウトプット先が Elasticsearch なので書き込みはできるのですが(スキーマレスに書き込みができるため)、 求人の項目をMapで表現しているためコード上で求人の各項目の管理難しく、さらに検索エンジンで使われていない 項目のフィールドも存在しておりました。 一方でUnitテストがしっかり書かれていたので、Unitテストを通してコードを拡張し保守運用ができている状態でした。 またパイプライン処理が型で抽象化されており、全体的なシステムの構造として理解しやすい作りになっておりました。 おかげで5年経っても全体的なコードの見通しは良い状態でした。 /** * パイプライン処理で使用する1つの処理を表すトレイト。 * * @tparam IN 入力パラメータの型 * @tparam OUT 出力パラメータの型 */ trait Stage[IN, OUT] { /** * このステージでの処理をこのメソッドに実装します。 * * @param in 入力パラメータ * @return 出力パラメータ */ def process(in: IN): OUT /** * このステージの次に別のステージを連結してパイプラインを構成します。 * * @param nextStage 次のステージ * @tparam T 次のステージの出力パラメータの型 * @return パイプライン */ def |[T](nextStage: Stage[OUT, T]) /** * このステージの次に別のステージを連結してパイプラインを構成します。 * このメソッドで追加されたステージは | で連結されたステージとは異なり、 * 前のステージで例外が発生した場合でも必ず呼び出されます。 * * @param nextStage 次のステージ * @tparam T 次のステージの出力パラメータの型 * @return パイプライン */ def >[T](nextStage: Stage[Either[ExtractorThrowable, OUT], T]) } どのようなプロセスで進めたのか? 以下の手順でシステムの作り直しを行いました。 既存のコードをチーム全員でコードリーディングし、何をやっているのか?何のためにあるのか?必要なのか?を議論してコード上での共通認識をもつ 求人データで使われている項目(必須、非必須)、使われていない項目の整理 ドメインモデルを定義する パイプライン処理のフローで型を定義する 既存のドメインロジックで使えるコードを移植する Unitテストを書く 実際に検証環境で運用して想定してなかった型がきていないかなどの確認 各段階の詳細は省きますが、1の項目は既存のコードを熟知しているメンバーから機能の必要性を的確に判断してもらい そしてコード上の知見、課題がチーム全員に共有されたのでよかったなと思っています。 また、 es書き込みバッチ ではUnitテストがしっかり書かれていたのでコードの移植もスムーズに行えました。 メソッドのアウトプットに対して型定義する リファクタリングで心に残っているものについて紹介します。 es書き込みバッチ では、 JSON形式の設定情報によって、様々なデータの形式を抽出する汎用的なメソッドがありました。 protected def applyScript(property: ExtractProperty, rawdata: Map[ String , String ])(value: String ): Option[Any] 上記のメソッドではInt型、String型、Seq型、給与を表す型、JavaのList型など様々な型のアウトプットを返し、その結果返り値が Option[Any] の型で、どんな値でも取れるような作りになっておりました。 しかしアウトプットの型がブラックボックス化して、コードが追えなくなるため返り値に対して型定義を行いました。 // 結果を表すトレイト trait ScriptResult sealed abstract class MVELResult extends ScriptResult object MVELResult { case class StringResult(value: String ) extends MVELResult case class IntResult(value: Int) extends MVELResult case class SeqResult(value: Seq[ String ]) extends MVELResult case class JListResult(value: java.util.List[ String ]) extends MVELResult case class ArrayResult(value: Array [ String ]) extends MVELResult case class SalaryResult(value: Option[Salary]) extends MVELResult } //ScriptResultはMVELResult以外の型も返すことがあるため、MVELResultとScriptResultでわけております。 //ScriptResultをミックスインした結果の型定義を行うことで、他の文脈の型もとることができます。 上記は、コードの一部になります。 リファクタリングしたメソッドのシグニチャ private def extractProperties( extractDefinition: ExtractDefinition, crawlingJob: CrawlingJob, parser: Parser ): Map[PropertyName, Option[ScriptResult]] これでメソッドの結果の型が明確になり、コードが追いやすくなります。 リプレイスを行った結果 今回の es書き込みバッチ は2ヶ月ほどでリプレイスを行うことができました。(コードの変更が1ヶ月、インフラの変更からリリースまで1ヶ月ほどでした。) またメソッドに限らず、ドメインモデルを定義することで、処置中のコードも以下のような型が定義されて、各パイプラインでのデータの変化がわかるようになりました。 ドメインモデルを定義 // クローリングデータの求人情報 case class CrawlingJob( documentId: DocumentId, crawledResult: CrawledResult // 省略 ) // ETLを行った後の求人のクラス case class ExtractedJob( jobId: JobId, jobTitle: JobTitle // 省略 ) リプレイス後の各パイプラインの返り値の型 // jsonから求人データの抽出した後の型 Either[Exception, CrawlingJob] // フォーマットチェック後の型 Either[ExtractStageException, Job] 型ない状態だと実際のデータにどういった型が入っているのかを常に考えながら実装する必要があり、非常に頭を使います。 一方で型としてコードに落とし込めると、型情報を見ながら進められるので考えることを減らすことができます。 他にもリプレイスを通して以下のことを行いました。 これまで依存している外部ライブラリの都合でScalaのバージョンが2.11系だったので、リプレイスを機にメンテナンスされていない外部ライブラリを切り離しScalaバージョンを2.13にあげることができました。 フォーマットチェックでは Validated を使って簡潔にエラーの蓄積を実装できました。 これまでEC2上にSparkのクラスターを作り運用していましたが、 AWS EMR を使いクラスター管理がマネージドになりました。 EMRクラスターの構築、ログ収集基盤の作成などは難易度が高かったのですが、マネージドになったことで特に手を加えることなく、クラスターを動かしているマシンが入れ替わっているので運用自体は楽になりました。 求人情報の正規化処理を別モジュールに分けて、求人を抽出するアプリケーションとしての責務に変更しました。 振り返り 5年前のコードであってもバージョンのアップグレードを行ったり、ドメインモデルを再定義することで既存のUnitTestを再活用することができ、コードを再生できました。 またこのリプレイスから2年ほど運用しましたが、ドメインモデルや処理の過程を型で定義することで求人項目の追加、削除しやすくなりました。以後も追加の開発を行えております。 Masterのcommitの状況 2021年以降から活発に開発も行われて、リプレイス後に開発の頻度が上がっていることがわかります。 スキーマレスなデータストアに書き込む仕様だと、型情報がなくても実現できるのですが、時間の経過に伴いメンテナンスが難しくなります。 その結果コードがレガシー化することにつながりやすいです。 もちろんまだまだ技術的な負債は存在している状態ですが、今回ドメインモデルとして型を定義して、責務を分解し、Unitテストを書くことで 過去のシステムの資産を使いながらコードを拡張できる状態にできました。 継続的な開発を行っていくために改めて、型定義、責務の見直し、Unitテストが重要であるということを認識しました。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは、スタンバイでデザイナーをしている清水と申します。 この記事では「スクラムをやっていたら、なぜかデザイン作業の生産性が上がった..!」という驚きを共有させてください。 スクラムとの出会い 私はウェブ業界で、かれこれ 10 年ほどデザイナーやフロントエンドエンジニアのような業務をしております。 その期間のほとんどではスクラムやアジャイルといった手法は使わず、ウォーターフォール寄りに仕事をしていました。 そんな私ですが、スタンバイに入って突然スクラムに放り込まれました。 最初はカタカナ語の多さや、スプリントの短さに面食らう場面も多かったですが、続けているうちに徐々に良さが分かってきた気がします。 特に、スクラムがデザイン作業に与える良い影響を実感しており、本記事では「スクラムでデザイン作業の効率性が上がった!」というテーマに絞ってお話しします。 デザイン作業の生産性を高めるスクラムのプラクティス 「スクラムのすべてのプラクティスがデザイン作業の生産性を高める」といっても過言ではない可能性がありますが、特に影響が大きいと感じるものを主に紹介していきます。 作業量の見積もり まずは作業量の見積りについてです。 私たちのチームではストーリーポイントを使い、スクラムチームの全員が集まって作業を相対見積もりしています。 このストーリーポイント付けを通して、デザイン作業の生産性向上につながる情報を得られます。 以前の私は「このデザインなら実装そんなに大変じゃないかな」と思って、デザインを作成することがよくありました。 しかし、実際にその作業にどれぐらいの工数がかかったかは確認できていませんでした。 ストーリーポイントづけをメンバー全員でやると、この状況が変わります。 エンジニアがストーリーポイントを付ける場面を一緒に見るため、事前に想定していたエンジニアの作業量と、実際に必要な作業量の乖離に気づけるのです。 この気づきが積み重なると「こういうデザインは実装の負荷が高いから、ここはちょっと変えておこう」などの工夫がしやすくなっていきます。 そうなってくると、デザイン作成の精度も上がり、エンジニアから喜ばれるデザインを作りやすくなっていくでしょう。 ユーザーストーリーの書き方 私たちのチームでは、ユーザーがやりたい最小のアクション、ビジネス価値がある最小の単位でユーザーストーリーを記述し、チケットで管理しています。 その際は、推奨されているプラクティスを踏襲し、タイトルを「{ユーザーの種類}として、{達成したいゴール}をしたい。なぜなら{理由}だからだ」のように書いています。 例えば「はじめてアルバイトを探す大学生として、未経験 OK なバイトを探したい。なぜなら、初めてのバイトで不安だからだ。」などです。 この「フォーマットに従ってストーリーのタイトルを記述する」という工夫によって、デザイン作業の生産性が向上します。 フォーマットに則ってタイトルを書き、そのタイトルを意識しながらデザインをすると、ターゲットや目的を改めて意識して検討を進められます。 その結果として、適切な解決策を提案できたり、そもそもの課題に対応した解決策が見つかったりする可能性が高まるのです。 ターゲットや目的を考えてデザインを検討するのは当たり前ですが、忙しい業務の中では忘れてしまう場面も多いのではないでしょうか。 スクラムはこの基本をしっかり続けるのに適したフレームワークです。 そしてスクラムを継続すると、ターゲットや目的を考えることが無意識にできるようになり、結果としてデザインスキルの向上にもつながると感じています。 スプリントレトロスペクティブ スクラムではスプリントごとに、レトロスペクティブ(振り返りのようなもの)を実施します。 レトロスペクティブでは、人・関係・プロセス・ツールの観点、うまくいったこと・改善が必要なことなど幅広い観点で振り返りを実施します。 私がスクラムではない現場で経験していた振り返りは、頻度が少なく(四半期に 1 回や、プロジェクトの終了後だけなど)、細かいプロセスの改善につながる内容は出づらかった印象です。 スクラムのレトロスペクティブは頻度が多いこともあり、細かいプロセスの改善につながる意見も多く出ます。 また、職種横断でチームを組んでいるため、幅広い観点で意見が出ます。 普通に仕事をしていると、デザイナー以外の職種から、デザインデータの作り方などに関するフィードバックをもらえる機会は多くありません。 一方、スクラムではフィードバックをもらえる機会がとても多く、生産性の向上、自身の成長に繋がっていると感じます。 最後に 上記で紹介した以外にも、スクラムの様々な特徴やプラクティスはデザイン作業の生産性を高めてくれます。 この事実に気がついた時は不思議に思いましたが、スクラムは「価値のあるソフトウェアを早く継続的に提供する」ことに主眼を置いたフレームワークなので、その目的のためにデザイン作業の効率が向上するのも当たり前と言える気がします。 この記事を読み、スクラムなチームでデザインに取り組んでみたい!と思ってもらえたり、スタンバイでのデザイナーの働き方などに興味を持ってもらえたりしましたら幸いです。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは。スタンバイで Engineering Manager(以下EM)を担当している高原です。 今回はスタンバイのプロダクト開発がとっている組織体制とそのねらいをご紹介させていただきます。 そして、現実に生じている課題、講じている対策についても触れさせていただくことで、よりリアルな開発組織の現状と今後の展望を共有させていただこうと思います。 スタンバイのプロダクト開発体制 Product Drivenを実現するための組織ビジョン スタンバイでは会社全体として“Product Driven”を掲げており、開発組織はプロダクトロードマップの実行に責任を持ち日々開発に取り組んでいます。 プロダクトロードマップを達成するための各テーマ毎にチームを構成、役割・責任・権限を明確化することで、それぞれのチームが自らミッションや目標を定義し、メンバーが一丸となって目標に向かって取り組む体制で開発を進めています。 また、開発組織ビジョンに掲げている「自立型組織」として各チームが動けるよう、開発プロセスにおいても、は各チームで最適な手法を追求することを推奨しています。 開発組織ビジョン「自立型組織」 なお、「自立型組織」は「自律」のミスタイプではなく、「自立」で合っています! 広辞苑からそれぞれの意味を引用させていただくと次のようになります。 じりつ【自律】 自分の行為を主体的に規制すること。外部からの支配や制御から脱して、 自身の立てた規範に従って行動すること。 じりつ【自立】 他の援助や支配を受けず、自分の力で判断したり身を立てたりすること。ひとりだち。「経済的に―する」 CTO明石の言葉を借りれば、「自律」は『成人や社会人として、あたりまえの観点』という位置づけで、そのうえ更に『ひとりだち』している状態を目指すという意図を表しています。 「自立型組織」に添えてある「個々人が事業を深く理解し、個々の専門性を発揮し、自らの裁量を持って実行できている組織」は、その『ひとりだち』が実現した状態を言い換えた文章になっています。 開発組織ビジョンの図で特に該当するところを青字にしたもの 導入している組織体制と仕組み ドメイン構成に沿ったチーム体制 ところで、現在の組織体制に変える以前は、プロダクト開発全体でLeSS(Large-Scale Scrum:大規模スクラム)を実践していました。 当時は、大半の開発メンバーが3〜5チームに分かれて所属し、全チームがスタンバイの全てのサービスの開発・運用に携われる状態(スタンバイ全体を対象範囲とするフィーチャーチーム)を目指す体制をとっていました。 しかしながら、スタンバイが提供する検索サービスはビジネスドメインも技術ドメインも広範囲に跨がっているため(下図参照)、これらを包括的に開発・運用できるフィーチャーチームの実現というのは、かなり難易度が高い状態を目指していたと言えます。 技術ドメインの構成イメージ そのため、実際にはどのチームにも得意な技術ドメイン・サブシステムがあるいっぽうで、いつまでも触ることができない技術ドメイン・サブシステムもあるという状態を抜け出せていませんでした。いま思えば、これらの技術ドメイン全体をカバーするフィーチャーチームを目指すことは(例の“チームトポロジー”でも指摘されている)「認知負荷」が相当高い組織体制を目指していたと言うことができます。 2021年1月に現CTOの明石が就任後、この問題に着目して組織再編に着手し、新年度を迎えた2021年4月から技術ドメインに基づいたチーム体制へと移行しました。 チーム横断コミュニケーション施策 同時にこれらのチームを横断するコミュニケーション設計を行って、チームの細分化と裁量の付与によって起こりがちなサイロ化や意志決定の遅延を防止するために、相互に状況を把握したり共通の課題に対応したりするための基盤となる仕組みを導入しました。 具体的には次のようなもので、現在も(都度調整を加えながら)運用しています。 最低限のルール整備と情報のフルオープン化 ・各チームのSlackチャンネルはpublicで作成、原則全員参加 ・横断会議体のオープン化(参加自由、議事録開示) ・全OKRを可視化 チーム横断案件の相談先とアジェンダの透明化 ・Monthlyでプロダクト開発全体に関わる情報共有や表彰などを行う会議を開催 ・Weeklyで全チームのOKR進捗会議を開催 ・DailyでプロダクトマネージャとCTOといった意志決定者が揃う(議題持ち込み制の)相談時間を確保 根幹となるプロダクト開発行動指針 そして、コミュニケーションの共通言語を作るべく、プロダクト開発における行動指針を作成しました。 これは個人とチームの行動とマインドセットに関する期待を言語化したもので、「START」の5つの頭文字で構成しています(「S・T・A・R・T」それぞれが示す内容については、以前の記事『 スタンバイのプロダクト本部の行動指針「START」と「Engineering Belt」とは? 』をお読みいただけると嬉しいです)。 この行動指針「START」が会話の端々に出るレベルに浸透するため、月次で行っている表彰の選考観点や、採用・評価・育成・人材配置の観点にしています。そして、このTech Blogなど社外に情報を発信する際の規範とするなど、常にプロダクト開発活動を照らす鏡として使うようにしています。 プロダクト開発行動指針「START」 なかでも、横断コミュニケーションの基盤として=サイロ化や意志決定の遅延を防ぐものとして次のように行動指針が対応しています。 Transactive memory(どこの誰が何に詳しいかの理解)を活用して協力を仰ぎながら Scientific(科学的)な数字・データを共通言語として使うことで、様々な立場のメンバー間の判断のブレを無くしてコミュニケーションロスを減らし、 Relevantに(自分ごと化して)染み出して行動できるようにする 以上でご紹介させていただいたような仕組みを基盤として、各チームが担当ドメインにおけるプロフェッショナルへと進化し、それが更に個々人のレベルまで落とし込まれた状態の「自立型組織」を目指して、2021年の4月に現体制のスタートを切りました。 2021年4月に掲げた「目指す開発組織の姿」 生じた課題、講じている対策 このような開発体制と仕組みを導入しましたが、さすがに「全てが順調に自立型組織に向かっています!」というわけではありません。 当初「目指す開発組織の姿」で掲げていた「1年後の状態」に対して、既に1年半が経過している現状はどうか?という観点で振り返って、生じてきた課題を飾らずに書き留めておきたいと思います。 横断案件の難航 複数チームが協力して実現する必要がある開発案件において、関係メンバーが皆、自チームの開発案件と掛け持ちするかたちになり、「船頭不在」状態に陥った。 (それでも自分ごと化して先頭に立ってくれるメンバーがいたものの)担当外ドメインの要件定義やアーキテクチャ設計が手探りになったり、意志決定機関が不明瞭でスタックしたりした。 異なる時間軸で開発するチームが集まって協働する開発プロセスの確立や、共通理解をとるコミュニケーションにスピード感が出なかった。 これらの課題に対して、既に対策を講じているものをいくつかご紹介します。 プロダクトマネージャ直下にプロダクトマネジメントオフィス(PdMO)という独自組織を設置して、チーム横断の課題管理を行うことにした チーム横断イシューをバックログ化して、各チームのバックログのEpicレベルや各チームのOKRと紐付けられるよう可視化し、全体整合性をとるようにした チーム横断イシューの責任者(いわゆるプロジェクトリーダー)を指名して、複数チームで連携する開発スケジュールの見積りや進捗状況の集約する役割を明確化した 生じた課題と既に講じた対策 もちろん、これで十分とは考えていませんし、今講じている対策も含めて振り返り、UPDATEし続ける必要があると考えています。 今後に向けてまだまだ先にある目指す組織の姿に向けて加速していくために、当面は次のような課題に向き合っていかなければと考えています。 チーム間で共用できる「型」の導入 各チームの専門性を活かした提案や指摘を結集して要件定義や開発計画を行える開発プロセスの前半を再定義するとともに、要件や背景の透明性と相互理解度を高める共通フォーマットを導入する そうすることで、結果的にチーム間のコミュニケーションコストを下げ、そのぶん仮説検証のスピードを高めたい 事業とプロダクトの未来像の透明化 CxOやプロダクトマネージャらが描く事業やプロダクトの未来像の透明性(⇒メンバーの理解度と納得度)を高める仕組みと、それによって質が高まることが期待されるボトムアップ型の提案と議論のハードルを下げる仕組みを構築する そうすることで、いま対策で入れている「横断イシューのバックログ」を廃止したい(バックログがあると、そこに書いてあるwhatを見がちになり、whyを見ての革新的な提案やトライアルが出にくくなるリスクを感じているため) こうやって(改めて・再び)、チーム横断で行うトップダウン的なマネジメントを減らして、チームの自立度を高めていきたいと思います。 いま少し引いて眺めてみると、共通の「型」を持たないまま独立性の高いチーム体制で走り始めた後、チーム間で連携が必要になった際に、コミュニケーション・インタフェースをいちいち構築する必要が発生しているというのが現状のように思われます。 そのために最小限の「型」を入れようとしていますが、決してトップダウンで物事を決めたいわけではないので、今後「型」の機能レベルが高まっていけば、集中管理的な仕組みをどんどん外していくべきだと考えています。 そして、権限を分散させ、集合知を最大限に活かす、すなわち「自立型組織」に、どんどん近づけていきたいと考えています。 このようにまだまだ成長途上にあるスタンバイのプロダクト開発組織です。 これからも、ものづくり・事業づくりの効果を高めるための仕組みづくりを考え続け、トライアルし続けていきたいと考えていますので しばらく走って・・・また経過報告をさせていただければと思います! 以上、ここまでお読みいただきありがとうございました。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
この記事を書いた目的 初めまして、上野と申します。求人検索エンジンを開発している株式会社スタンバイにて、BtoCプロダクトのスクラム開発チームでプロダクトオーナー(Product Owner:PO)を担っています。キャリア的には広告代理店が長く、直近までスタートアップ支援をしてきており、事業会社でスクラムでのプロダクト開発経験は初めての経験でした。 あまり知られてないのですが、実はスタンバイは歴史的にスクラムに関する教育や開発の環境が非常に整っています。僕自身もありがたいことに Odd-e社 様の 認定スクラムプロダクトオーナー(CSPO®︎)トレーニング や同社CTO 貝瀬様 のコーチングも数ヶ月にわたって受けることができ、自分なりのプロダクトオーナー像が見えてきた頃でした。そこへちょうど今回のようなチャンスがあり、本記事執筆に至っています。 この記事で伝えたいこと 基本的にはスクラムにおけるプロダクトオーナーで僕が常日頃心がけていることやtipsを経験からお伝えします。どちらかというとマインド部分が多いのと、なるべく読了後にすぐ実践できることをコンパクトに書いているつもりです。 欲を言えば、スタンバイに興味を持ってくれる人がいたら嬉しいなと思っています。 逆に書かないこと スクラムの教科書的な用語解説や小難しいことは書きません。「スクラムとは?」や個別のスクラムイベント、HOWなどについても割愛します(今後記事化する予定です)。ほかで体型的にまとめられている方もたくさんおりますし、僕自身がそうなのですが、小難しいことって、学んでも実は現場に活きづらかったりしますよね。結構この記事を読んでプロダクトオーナーとして明日からどう生かすか、の方がお役に立てるのではと思い、使えそうなことを中心にまとめてみました。 僕の考えるPO(プロダクトオーナー)十則 いきなりですが、僕の考えるプロダクトオーナーが守るべき「十則」をご紹介します。 PO(プロダクトオーナー)十則 1. 四六時中ROIを意識していること 2. 長期、短期それぞれでROIを意識できていること 3. ROI最大化のための仕組み作りができていること 4. 明確な判断基準と毅然としたリーダーシップがあること 5. 目的&目標を実現するためには手段を厭わないオリジナリティと度胸があること 6. 合理的で否定されないロジックを持ち続けられていること 7. 適材適所、且つ楽しく気持ちよく周りが働けるような環境作りができていること 8. 適切なフィードバックが実現できていること 9. 常に自身を高め続けられていること 10. どんな状況でも1歩でも前に進めるような言動ができていること 僕が1年近くプロダクトオーナーをやって思った結果がこれです。 正直、プロダクトオーナーは誰でも名乗ることができます。ただ、役割を全うするには弛まぬ努力が必要だと言うことを痛感しました。 ROIを最大化するためには手段を選ばない プロダクトオーナーとは、スクラムチームから生まれるプロダクトの価値を最大化することの結果に成果を持つ人、つまりは「 プロダクトのROIを最大化する 」ことがミッションである人のことです。 「スクラムを始める際には、まずプロダクトオーナーを任命すること」と言われているほどスクラムチームには重要な存在であり、プロダクトバックログを左右する最終意思決定者のため、複数人いてはならず、必ず1スクラムチームに対して1名です。開発者やスクラムマスターなどとは利害関係が異なるので、兼務も非推奨と言われています。 ROIを最大化するための決まった手段はありません。つまり、あらゆる手段を講じることができる、もっと言えば、そのくらいオリジナリティを発揮すべき役割であると考えています。他社や他チームのノウハウも積極的に参考にすべきですし、人が足りなければ他チームに交渉するし、ROI的に、今はチームを休ませるべき!と判断すれば1か月休ませたって良いでしょう。 「ROI」と一口に言っても、取り扱う際は注意が必要です。 -Investmentはプロダクトオーナーが直接コントロールできる? -何に対してのReturn? -どの時間軸でのInvestmentとReturn? 予算追加や人員増、ツール購入、計画など時間やお金を投資すべき対象はさまざまですが、Investmentはプロダクトオーナーがコントロールできるものでなくてはなりません。 直接Investすることで返ってくるReturnを最大化するのです。そのReturnは必ずしも売上等の定量的な事業インパクトに限らず、教育や文化形成など、社内向けの定性的なReturnがあることも忘れてはいけません。 見落としがちなのが、いつ発生するReturnなのか?です。次のスプリントで反映されるんだっけ?数ヶ月はたまた数年後のReturnなんだっけ?という点はROI最大化を考える上で非常に重要なポイントです。 また、ROI最大化に向けて、僕自身プロダクトオーナーとして心がけていることの1つは、多少の強引さです(笑)。スクラムチームメンバー全員に意見が賛同されなくても 「否定されない」程度のロジックを詰めて進めていく 、くらいの心構えも必要だなと感じています。とはいえ、やりすぎると単なる「わがまま」なので、伝え方や一貫した戦略はセットにはなりますが。サッカーでいうと、点さえ取っていればさほど文句を言われない、そんな世界に住んでいるのがプロダクトオーナーなのではないかな、と考えています。 つまるところ、プロダクトオーナーはROI最大化に向けて一歩でも二歩でもスクラムチームを前に進め続けること。転んでもタダでは起きないこと、本気でROI最大化を突き詰めるのであれば、そういったスタンスが必要とされていると感じています。 プロダクトオーナーとしてやらないこと 逆に、唯一僕が「 やらない 」と心がけているのは HOW(どうやるか?)まで踏み込むこと です。プロダクトオーナーはWHAT(何をやるか?)とWHY(なぜやるか?)のセットでスクラムチームを動かす方が何かとうまくいくと言われています。 つまりは、何を、なぜ実現したいか?というストーリーをプロダクトオーナーが考え、語り、どのように実現したいか?は開発者に委ねるということです。チームによっては歓迎されることもあるのでしょうが、プロダクトオーナーが開発にまで介入してしまうと、開発者の心情的には「言われたことだけをやっている」感が増すことが多いと言われています。経験的にも僕は控えてきました。 上記を徹底するために実践しているのは以下です。 1. PBI(Product Backlog Item)にユーザーストーリー(WHAT)を記載すること 2. 目でも耳でも背景(WHY)を徹底的に共有すること 1つ目は、PBIは必ずユーザーストーリーにしていることです。タスクを書くことはまさにHOWまで入り込んだ行為です。実現したいこと(Tobe)さえクリアであればスプリント内に最速で実現する方法(To-do)は開発者が検討すれば良いのです。 ちなみに、ACで実現したいことを書くだけでは開発者が判断しかねるケースも出てくるので、「やらなくても良いこと」を敢えて記載することもあります(AC1つに対して「やらなくても良いこと」2つが黄金比らしいです)。 2つ目は、実現したいユーザーストーリーの背景(なぜそれを実現したいか?)も、しっかりPBIに記載しつつ、納得いくまで説明することです。プロダクトオーナーがPBIを書くこともあるので、PBIは記載内容を型化するのも良いでしょう。 ただ、PBIの記載やスクラムイベントだけではどうしても伝えきれないストーリーの背景もあるはずです。ユーザーインタビュー結果のまとめやログ分析結果、競合情報など、共有したいんだけど、どこで話せばよいかわからない、といった類の情報も多い気がしています。 そのため、僕のチームでは、オンラインワークであることをうまく利用し、discordで「POラジオ」なるものを実践しています。週に30分ほど、プロダクトオーナーとプランナーがマーケットやユーザー動向、中長期で実現したいことなどの自由な雑談をただ単純に聞いてもらうという会です。任意参加なのですが、少々複雑な取り組みにおいても意思疎通がしやすくなってきている実感はあります。開発者からも継続希望の声が多く、数か月続けてこれています。 バックログ管理とその先にある大事なコト ここでスクラムガイド書かれているプロダクトオーナーの責務に触れておきます。「プロダクトバックログ管理」です。 “『プロダクトオーナーは、効果的なプロダクトバックログ管理にも責任を持つ』” PBI自体は誰が作っても並び替えても問題ありませんが、最終的に優先順位の意思決定はプロダクトオーナーに委ねられています。その優先順位の基準はROIが最大化されること、且つ、そのロジックはブレることなく、常に一貫させることが必要です。 ただ、個人的に、バックログの優先順位付けよりも重要だなと感じているのは、その先にある 開発者とのコミュニケーション です。優先順位が付けられたとしても、開発者が動けなければ意味がありません。プロダクトオーナーは何も生み出せないのです。プロダクトを前に進めるため、開発者が気持ちよく働ける環境を作っていくこともプロダクトオーナーにとって重要な役割であると考えます。 開発者の人柄や働く上での「やるべき」「やらないべき」ことをを知り、適切なコミュニケーションを取るよう心がけています。仕事とは言え、1人の人間です。プライベートな問題でどうしても気持ちが乗らないときだってあるでしょう。そうした心の変化も週次や隔週で1on1やオフライン出社を設けてコミュニケーションが取れる機会を作り、開発における障壁を極力無くすように心がけています。少し気持ちが乗っていなさそうだなということであれば負荷を減らしたり、逆に乗っている方には少々負荷を上げたりしています。 内容によって伝え方も気を付けています。例えば、良いニュースやポジティブなフィードバックは全体の場で盛大に話しますし、ネガティブなフィードバックが必要な時はなるべく個別で対面で話すようにしています。 DMを非推奨にしている企業さんもいらっしゃるようですが、全ケースに当てはめる必要はないと思っています。人によっては全体で語られることが嫌な人もいるはずで、特性に合わせて対応すべきでしょう。 ちなみに僕のチームでは四半期の最終日は全員で休暇を取るようにするなど、チーム独自のルールを設けたりもしています。 プロダクトオーナーとして意識していることとtips集 他にも書きたいことはたくさんあるので別記事にするつもりですが、今回お伝えしたかったのは、プロダクトオーナーを全うし続ける大変さとその反面自由度が高いゆえのやりがい、という点です。 つまるところ、プロダクトオーナーには特別なスキルや資格は必要なく、ROIへの執着、そして、プロダクトに対する熱い想いとチームメンバーへのリスペクトがあれば誰でもプロダクトオーナーを名乗れると思っています。 誰でも名乗れる、は裏を返せば、自分だけにしか出せない価値を常に問われていることに他なりません。「 プロダクトオーナーとしてアナタだけにしかできないことってなんなんですか? 」という呪いの言葉を常に視界の横にちらつかせながら活動していかなくてはならないのです。 オリジナリティを出し続けることは至難の技だとしても、少なくともプロダクトオーナーとしての自覚は持ち続けようと、僕がいつも戒めのためにPCの横に貼っておこうと考えたのが以下プロダクトオーナーの十則というわけでした。 PO(プロダクトオーナー)十則 1. 四六時中ROIを意識していること 2. 長期、短期それぞれでROIを意識できていること 3. ROI最大化のための仕組み作りができていること 4. 明確な判断基準と毅然としたリーダーシップがあること 5. 目的&目標を実現するためには手段を厭わないオリジナリティと度胸があること 6. 合理的で否定されないロジックを持ち続けられていること 7. 適材適所、且つ楽しく気持ちよく周りが働けるような環境作りができていること 8. 適切なフィードバックが実現できていること 9. 常に自身を高め続けられていること 10. どんな状況でも1歩でも前に進めるような言動ができていること 上記はあくまで個人的な「十則」ではありますが、読者のPOの皆さんにとっての十則をまとめてみると、自由度が高い職務の中でも軸がブレずに取り組めるかもしれません( Twitterで見つけ次第 スタンバイアカウントがコメントするかもしれません)。 ちなみに、Odd-e社様が「 プロダクトオーナーチェックリスト 」というチェックリストを外部公開しているのでご興味あればぜひご確認ください。 最後に、本文でも「プロダクトオーナーとしてやらないこと」で触れた、僕がプロダクトオーナーを担う中で上手くいったtipsを少しだけ紹介します。 反響次第では関連記事を公開していこうと思いますので、感想等ぜひお待ちしております。 まとめ:スクラムtips 課題感 開発者にとって、POやプランナーが考えていることがわからないので、開発にアイデアを活かしづらい。 1.POラジオ 解決策 任意で毎週2回30分、discordでラジオ番組風トークを開催。 開発チームは耳だけ参加でOK。POやプランナーは直近のプロダクト課題やロードマップ、分析結果など、普段ビジネス側で会話するような雑談を準備なしに実施。 以下プロダクトオーナーtimesでつぶやいたことを拾って話したりもしている。 結果 比較的課題の目線合わせがしやすくなったと感じる。 開発者からも評判は良く、継続を求める声が多い。 2.プロダクトオーナーtimes 解決策 slackに公開チャンネルを作り、プロダクトオーナーとして考えていることをTwitter感覚でslackにひたすら呟く活動。市場や気になるニュース、SEO、ユーザー体験、ログ分析結果、ほか業務以外の雑多なトピックなど、1日1投稿以上は心がけている。 他チームのPOらも参画。 結果 表にされづらい自グループのPOの考えを開発者や他メンバーが追いやすくなり、コミュニケーションにおける前提説明が減った(メンバーから「この前timesに投げてたアレのことですよね?」などで話をつなげやすくなったり)。 開発者との有益な会話に発展しチケット化につながるだけでなく、他グループと連携して施策につながるような副次的効果も。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは。スタンバイで Engineering Manager を担当しているステファンと高原です。 今回は、先日開催された「スクラムフェス仙台」で紹介させていただいた私たちの活動についてお伝えしたいと思います。 登壇の背景 2022年8月26日〜27日に開催されたスクラムフェス仙台のキーノートスピーチにOdd-e Japan代表の江端様が登壇するにあたって「スタンバイの事例も紹介したい」とのお声掛けをいただき、私たちもゲスト登壇してお話しさせていただきました。 Odd-e Japan社にはスタンバイのアジャイルコーチ・アドバイザーをお願いしています。 代表の江端様は本稿執筆時点で日本でただ1人、国内に限定されない Certified Scrum Trainer® として認定されているアジャイルコーチで、スタンバイのスクラム導入にも手厚いご指導をいただいておりまして、その熱さは当時を知るメンバーの語り草になっているほどです。 今回の江端様のキーノートスピーチのタイトルは「Various Scrum Teams」。 主催者さんからの「ソフトウェア開発以外での活用事例、活用方法を話してほしい」という要望に応えて数社のスクラム活用事例を紹介するものでした。その一社として参加させていただいたかたちです。 スクラムフェス仙台 www.scrumfestsendai.org スクラムフェス仙台2022 - 江端様キーノートスピーチ概要 confengine.com 事例紹介の概要:私たちCTO室〜EM室の業務をスクラムで進めています そのなかで私たちが紹介したのは、日々私たちが所属チームで行っている業務の進め方に関してでした。そんな話の何が面白いとされたのか?まず私たちのチームの立ち位置や業務についてご説明したいと思います。 チーム名称 CTO室(2021年4月〜)→現在は EM室(2022年4月〜) チームの立ち位置 私たちのプロダクト開発組織では、プロダクトロードマップを達成するための各テーマ=技術ドメインごとに開発チームを構成しています。 図:プロダクト開発組織の構成イメージ 私たちのEM室はこれらの開発チームを横断的に支援する存在です。 チームのミッション エンジニアリングパフォーマンス発揮のための環境整備・組織運営施策・技術課題の解決促進、人材成長促進などを実施する 業務内容の例 ・OKR導入支援(既に手離れしています) ・エンジニア行動基準「Engineering Belt」の定義・導入・改定( 関連記事 ) ・プロダクト組織のアセスメント ・開発チームのプロセス改善支援 ・プロダクト開発組織共通会議体の運営(OKR進捗会議、TechLead会議、マネージャ会議、表彰イベント 等) そんな私たちのチームは、一般的なスクラムチームと次の2点が異なると言えそうです。 ・プロダクト開発ではなく、組織開発をミッションとするチーム ・プロダクトオーナー(Product Owner:PO)含めメンバー全員が100%専任じゃないチーム 補足:私たちはEM室のミッションとは別に、EMとして得意分野や担当範囲ごとに開発チームの支援(例えばメンバーとの1on1、開発チームのTechLeadとの協働、開発チームのPOやマネージャーとの協働など)を行っています。 私たちがスクラムを導入した背景 現在の組織体制になる以前、スタンバイでは大規模スクラム(LeSS)を採用していて、高原を含むスクラムマスター3名体制で取り組んでいました。それぞれに所属チームも持ちつつ、LeSS全体もみていて、POもサポートするというかたちでした。その当時、次のような理由で、スクラムマスター3名もスクラムチームとして活動することにしました。 ・スクラムマスター間で協力して対応するべき課題も相当数あり、課題管理・タスク管理などチームワークが求められる ・自分たち自身もスクラムを実践してカイゼンを繰り返すことで、開発チームの支援に繫がるような事例や知見を蓄積したい その後、上記スクラムマスター2名を加えて発足したCTO室(現EM室)でも、この流れを汲んでスクラムを継続しました。 私たちのスクラムで行っている工夫 このような流れで現在に至り、足かけ2年以上スクラムを続けていますが、やはり問題も生じて、カイゼンを加えてきました。私たちがカイゼンしてきたポイントを二つご紹介したいと思います。 1.ステークホルダーの考慮 2.Scientificなふりかえり それぞれを簡単にご紹介したいと思います。 1.ステークホルダーの考慮 RefinementとPlanningを中心に、ステークホルダーを考慮して行ってきたことがいくつかあります。 POのアサインはやはり最重要だった CTOが多忙だったため、当初それに配慮するかたちで、当初はEMの合議制でPOを代行するという体制を取りました。ところが、課題の背景を捉え切れていなかったり、ゴールに対する優先順位判断が不十分だったり、レビューを受けても個別断片的になったりと問題が噴出しました。この時点では、私たちの業務の司令塔であるCTOが、チーム外のステークホルダーと変わらない状態でした。 その反省から、改めてスケジュール調整をお願いするなどして、(ちゃんと)CTOをPOに迎えてチームを再始動しました。その後はベロシティがカイゼンしました。 複眼でのPlanning・Refinementの確保 チームのふりかえりで「毎スプリント積み残しが続いている」問題について話し合ったところ、「検討不足と指摘を受けたところは別のEMが見たら気付けていたはず」とか「ステークホルダーとの会議設定が遅れて確認が間に合わなかった」などの、いわゆる段取り不足が理由で完了できなかったという共通要因が浮かび上がってきました。言い換えると、「完成の定義を満たすために必要十分なサブタスクを作成できていない」という状況が見えてきたのです。 そこで、割り込み対応が入りがちで揃いにくかったEM間で「Planningを揃って行えるよう時間を確保する」、「Refinementは関係するEM2人以上で時間を合わせて行う」というWorking Agreementを追加しました。以降、完了率が格段にカイゼンしています。 相対見積りの観点の共通認識 先に会議設定が遅れた例を書きましたが、私たちが価値を提供するステークホルダーは、各開発チームのマネージャ・PO・Tech Lead、プロダクトマネージャ、部長陣、CTO、COO、さらに人事や広報なども加わることがあり、バックログアイテムごとにステークホルダーが大きく異なりします。また、プロダクト開発全体がスコープの課題では全開発チームのリーダーとコミュニケーションが必要だったりするので、相当な人数になるケースも多いです。 そこで、私たちの相対見積もりでは「ステークホルダーの数」が一つの軸として定着しています。プロダクト開発の相対見積りでは「技術の不確実性」と「要求の不確実性」とを軸に行なっているのですが、ステークホルダーの数は後者に該当するという整理をしています。 最後のポイントはCTO室・EM室という私たちのチームならではの特徴的なものかもしれませんが、同じように社内に対してサービスを提供するチームでスクラムを実践されていたら、似たエピソードがあるかもしれませんので、情報交換してみたいところです。 それ以外の2つは、どんなスクラムチームでも共通して当てはまる話だろうと思います。 2.Scientificなふりかえり 私たちは、月一回、客観的データを使ったふりかえりを行って、チーム活動をカイゼンしようとしています。 図:ふりかえりで使っているデータのイメージ 元々は、1でも挙げた「完了率が低い」という課題に向き合うためだったり、兼務メンバーばかりなので作業時間の認識をメンバー間で合わせておく必要があったり、といったきっかけで始めた取り組みです。 データを使うことで、次のようなメリットがあると考えています。 ・客観的事実を振り返ることができる バイアスを少なくできる ・全員が同じ対象に対して振り返ることができる 同じ対象をみて別の気付きが出たときは、他人の視点を知ることができる データの解釈は在籍経験やキャリアに依存しないので、誰でも気付きを挙げられる この「Scientificなふりかえり」については、登壇時に複数の方からご質問をいただいたり、その後のネットワーキングタイムでも何人もの方が話しに来てくださるなど、多くの反響をいただいたため、補足情報として共有させていただこうと思います。 fact を使ったふりかえり! 科学的なふりかえり、良い響きだ スクラムマスターが自動化されそう>Scientificなふりかえり Q:Scientificなふりかえり。どんな指標をお使いですか? A:コミットしたストーリーポイント/完了したストーリーポイントの差分、価値提供できるユーザーストーリー/そうじゃないタスクとのアイテム比率、などをみています。 Q:的を射たデータを取るためにしている工夫は?どうやって指標を選定した? A:Jiraのレポートで自動的に出ているようなものから始めて、自分達が課題を感じたものをどう測るか?で次のふりかえりまでに指標を作ったりもする。ふりかえりのふりかえりで、気付きが出ないデータは一旦お休みするなどして入れ替えも試みています。 Q:全部が全部 fact ベースになるとちょっと窮屈になるのでは? A:ご指摘のとおりだと思います。先ず、実際には私たちも、各自の「もやもや」を書き出すスペースとかも設けていて、Hybridで使っています。 また、データを使うふりかえりは実施間隔を少し空けて、それ以外のふりかえりと組み合わせて行うのがよいと考えています。 ちなみに、私たちは「月1回」データで振り返っています。というのも、毎スプリントのレトロスペクティブでデータの推移をみても大きく変化しないので、傾向が変わる可能性がある3-4スプリントくらい空けてみるくらいが、適切なサイクルではと考えています。 また、このように「先に事実から問題認識をしてからその時の感情を思い出す」という流れも、逆に「先に感情を認識してから問題となる事実を思い出す」という流れも、どちらもふりかえりとして有効だと思っていますし、以降の「要因を掘り下げて→対策を考えて→TRYする」という流れは共通すると考えています。 今後について というわけで、今回ありがたい機会を得て、自分たちの活動のふりかえりと整理にも繫がりました。今後に向けた野心(will)を書いて締めくくりたいと思います。 開発チームのScientificなふりかえりを支援したい 各チームの状況や扱うテーマによって見るべきデータも異なると思われるので、それぞれのチーム目標や開発スタイルなどに寄り添って、示唆や気付きを得られそうな指標やデータを探すところから伴走していければと考えています。 開発チームの good practice を紹介し合い、生かし合える機会や文化を作りたい 今回、私たち自身が事例を紹介させていただいたことで、社内でもチーム活動の工夫を共有しあえる場を作っていければ、と思いました。 私たちは各チームの「自立型組織」化を目指しているので、画一的ではない様々な取り組みがTRYされています。それらの「こんなことをやってみた」「やってみてどうだった」をお互いに共有して学び合う意義があるように感じます。 定期的に、俯瞰的な組織分析も行いたい 私たちはPO的に自分たちで仮説立ても行うべき立場なので、バックログに並んでいる既存の課題だけに視野を固定されてしまわないよう、半期や四半期ごとに組織全体をふりかえって俯瞰的に分析する機会を設けたいと考えています。 ひとまず、そのこと自体をバックログに積んでおいて、実施をリマインドされるようにしておこうと思います(笑)。 以上、お読みいただきありがとうございました。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
はじめに 求人検索エンジンスタンバイでは、広告枠が存在しています。 広告主様より広告枠に出したい求人票に対して入札額を求人に設定いただき、広告が表示されるようになっております。スタンバイではこの広告のクリックに対して収益が上がる仕組みを採用しています。 スタンバイのように検索に連動して広告を出すものを検索連動広告と呼びます。このような検索連動広告においては、検索の精度を担保する必要がありますが、同時に広告としての収益を確保する必要があります。 この記事ではスタンバイにおけるランキングと入札の仕組みについて紹介させていただきます。 ※ 入札額: 広告主様が設定した広告に対する最大予算 ※ 落札額: 1clickに課金される広告費 広告ランキング スタンバイでは落札額の決定とランキングにGSPを利用しています。 GSP(Generalized Second Price Auction)はセカンドプライスオークションの一種であり、ランキングと課金方式の決定します。 フィルター 検索条件に一致しない広告を表示してもユーザーに興味を持っていただけないため、検索にマッチした求人票だけがオークションに参加できるように絞り込みを行なっています。 ランキング フィルターに残った求人票の中でもより求職者にマッチした求人票を上位に出すためスタンバイではユーザーのクリック率を機械学習により予測しています。 予測したクリック率と、入札額等を利用して期待収益額を計算してランキングの上位に出す求人票を決定しています。これによりユーザーのニーズにあった求人票をランキング上位に挙げつつオークションを実現しています。 期待収益額 = 入札額 × 予測CTR 落札額の決定 スタンバイで採用しているGSPは、セカンドプライスオークションの一種であるため入札額がそのまま落札額になりません。 自分よりランキングが1つ下の求人票の期待収益額と同額となるように、落札額を決定します。 計算例 順位 入札額 予測CTR 期待収益額 落札額 1 35 0.30 10.50 34 2 25 0.40 10.00 23 3 20 0.45 9.00 20 落札額計算ロジック: 順位:1の落札額計算すると、10.00 / 0.30 = 33.33 となるので、落札額を34とする 順位:2の落札額計算すると、9.00 / 0.40 = 22.5 となるので、落札額は23とする まとめ この記事ではスタンバイ広告の検索精度を担保しつつ収益性を担保するための仕組みについて紹介させていただきました。他にもスタンバイで行われている技術などを紹介していますので興味を持っていただけたのであれば、他の記事もご覧ください。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
こんにちは。StanbyのDataPlatformグループでデータエンジニアをやっている陳です。 今回は、スタンバイ社が独自に開発しているリアルタイム分析プラットフォームのStanby Analyticsを紹介します。 Stanby Analyticsとは 何故Stanby Analyticsを作る事になったのか GAの廃止 Stanbyはサービス開始時からGoogle Analytics(ユニバーサル アナリティクス、以下UA)を使っており、UAをベースとした分析結果で事業判断を行なってきました。 しかし、Google社から2023年のUAサポート終了と、Google Analytics4 (以下GA4)の移行が案内されたことをきっかけに、社内でStanby Analyticsプロジェクトが開始されました。 GA4の金額が高い 全ての分析要件をGA4で満たすプランを検討した結果、スタンバイのユーザー数増加に伴って、全てのイベントログをGA4で取得するには高額になる為、内製化でコストダウンを期待できる事がわかりました。 GA4のカスタマイズ性が低い Google Analyticsは完成度の高いプロダクトですが、GA4単体ではStanbyの分析要件を満たせません。何か特殊な処理をしたい時、カスタマイズ性の低さがUA時代から課題になっていました。 データ基盤を統一したい スタンバイではAWSをベースに開発をしており、Data Platformグループが開発している基盤は全てAWS上にあります。GAはデータをGCPのBig Queryに保存するので、データ基盤が分断されている状態でした。GAのデータを他のデータと一緒に分析したい場合、Big Queryからデータを持って来なければならなく、工数がかかっていた事が問題となっていました。 Stanby Analyticsのアーキテクチャー アプリケーション層 フロントエンドにJSを埋め込み、フロントで発火した全てのイベントをログとしてサーバーサイドに飛ばしています Data Pipeline層 フロントエンドから来たイベントログは、API Gatewayを通しKinesis Streamに送られます。 Kinesis StreamはメッセージQueueの役割を果たしており、ここからバッチ処理とリアルタイム処理のパイプラインに分かれて行きます。 バッチ処理の場合 生データを簡単な分類をしてKinesis Firehoseを通しS3に書き込んでいきます。 リアルタイム処理の場合 Kinesis Analyticsを通して必要なビジネスロジックを全てonlineで処理をしていきます。例えばセッション周りのKPIを処理する場合、FlinkのSession Window Functionを用いて同一Sessionで発生した全てのイベントをAggregateし計算した後Opensearchにデータを書き込みます。なので指標によってmsレベルのラグと、ビジネスロジックに応じて数分 ~ 数十分ラグのパイプラインが同時に走り処理を行なっています。 下の画像はFlinkのパイプラインの1つですが、Pipeline上で色んな処理や集計を行なった上でデータの書き込みを行なっている図になります。 Data Storage & Computing層 バッチ処理の場合 S3に書き込まれたデータはAWS Glueで定期的に加工され、datalake(S3)に保存されます。分析者は基本的にAthenaを使いdatalake上のデータを分析する様になっています。 リアルタイム処理の場合 基本的に加工ロジックは全て前処理のパイプライン上で終了しているので、Opensearchの役割は簡単なフィルタリングと、書き込まれたデータの検索となっています。結果として複雑なQueryが実行される事はほぼなく、ニアリアルタイムな分析が可能となっています。 Visualization層 バッチ処理の場合 RedashやTableauを使用しており、データアナリストの方々に作成して頂いたダッシュボードでアプリケーションの状態を可視化し日々分析を続けています。 リアルタイム処理の場合 OpenSearchに付属したKibanaで可視化しており、今は一番基本的な指標のみを可視化しています。こちらは分析用途以上に、異常検知が目的となっています。 リアルタイム処理の異常検知 Stanby Analyticsにおけるリアルタイム処理の最大の利点が異常検知になります。 過去スタンバイでは、サービスリリース後異常値を検知する仕組みが整っていなく、ユーザー様に大きなご迷惑をおかけしてしまった経験があります。このような事態を防止する為、Stanby Analyticsが活用されています。 アラート閾値の設置が難しい スタンバイは求人検索エンジンのサービスであり、基本的に24時間365日の稼働をしています。当然ながら、1日の中ではアクセスが集中する時間帯、閑散する時間帯があります。ピーク時のアクセス数はオフピーク時の約10倍程になります。更に求人が活発になる季節性や、CM等による一時的なアクセスの急騰 もあります。下の画像では一日内の指標の変化になりますが、ピーク時とそうでない時間帯でかなりの落差がある事を示しています。 そんな中、アクセス数やクリック数の様な動的な指標の異常値を従来の固定閾値を設定する方法で検知するのはとても難しくなります。リアルタイムの異常検知において、ただ単に0, 1の様な死活監視だけでなく、急激なKPIの変化をいち早く検知し、アクションを起こす事が求められています。 KibanaのAnomaly Detection 上記の課題を解決する為、今回Stanby Analyticsで採用したのはKibanaのAnomaly detection機能です。 https://www.elastic.co/guide/en/kibana/current/xpack-ml-anomalies.html 注: AWSのOpenSearchサービスでは、7.10バージョンのKibanaしか提供していない為、Stanbyでは7.10のKibanaを使用しています。 アルゴリズムに関する具体的な説明は割愛させて頂きますが、KibanaではRandom Cut Forestを使用しており、教師なし学習を用いて直近のWindowサイズ内のデータと比較する事により高速に異常値の発見を可能としています。 https://aws.amazon.com/jp/blogs/big-data/using-random-cut-forests-for-real-time-anomaly-detection-in-amazon-opensearch-service/ Anomaly Detectionのパラメータ数は多くなく、簡単な閾値の設定だけで始められるお手軽な機械学習機能となっています。 Anomaly Detectionの運用 結論から申し上げますと、現状プロダクション環境で異常検知が出来てるかはわかりません。 理由はまだリリースして数ヶ月程度なので、プロダクションレベルでの異常が発生していないからです。(いいこと) なので今回はプロダクション環境で意図的に異常値を発生させた時の結果となっています。 19:00にOpenSearchへ書き込むデータ量を半分にした所、数分でAnomaly Gradeが上がり、20分程度で 異常レベルが70%を超え、アラートが発火されます。今回はリリース作業等で一時的な異常を無視する為、敢えて20分以上異常が続く場合のみ発火させるようにチューニングしています。 その後もデータ量を半分のまま暫く流し続けると、徐々に新しいデータ量に対しての学習が進んで行き、数時間後には正常値とみなしてアラートが止まっている事がわかります。 Stanby Analyticsの展望 以上がStanby Analyticsの現在となっていますが、まだまだ開発途中のプロダクトで、日々チームメンバーと議論しながら改善を行なっています。 Stanby Analyticsの将来像: Flink SQLの実装 現在のKinesis AnalyticsはScalaのアプリケーションで書かれていますが、作ってみたら意外とFlinkSQLで代替できそうなコードが多かったです。FlinkSQLで作る事により、Batch処理と同じコードでロジックのメンテナンスが可能です。 LakeHouseの導入 現在のBatchシステムはHourlyの集計しか行なっていません。Athenaで他のデータと分析する際には1時間のラグが発生します。HudiやIcebergといった仕組みを用いて、Batch集計のニアリアルタイム化を目指しています。 データ分析の民主化 現在、新指標の追加やダッシュボード作成はエンジニアが主体で動いています。データドリブンな文化を目指すには、誰でもデータを扱える民主的な環境提供が必要不可欠です。データプラットフォームのエンジニアだけでなく、誰もが簡単にデータ分析を始められるプラットフォームを目指しています。 最後に Stanby Analyticsの開発によって、UAでは解決困難だった分析やコスト面の課題をクリアし、更にリアルタイムなアノマリー検知の仕組みにより、より迅速且つ柔軟な異常検知が可能となりました。 Data PlatformチームではStanby Analyticsの開発、運用だけでなく、社内のデータ基盤の改善やデータパイプラインの構築等沢山の業務に関われます。スタンバイ社のデータ活用支援やそれ関係する技術領域に興味関心がある方は、是非一度お気軽にご相談ください。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
1 はじめに はじめまして、スタンバイのSearchAdvertisingCoreGroup(検索・広告コアグループ、以降SACG)でAPI・インフラ周りの開発を行なっている金正です。 この記事では、スタンバイにおける検索への取り組みを紹介します。 2 一般的な検索システムに関して まず一般的な検索改善の取り組みを紹介します。 以下の図のように一般的な検索システムは大きく分けて2つのコンポーネントに分けられます。 2.1 クエリプリプロセス ユーザーが入力したクエリをより検索マッチしやすく加工したり、 ユーザーの検索理解をする、いわゆる「クエリアンダースタンディング」と一般的には呼ばれているコンポーネントもこのクエリプリプロセスに含まれます。 そもそも検索システムに使い慣れているユーザーなら、クエリアンダースタンディングは必要ありません。 検索窓されあれば自分で意図通りの検索クエリを入力し構築できるからです。 しかし全てのユーザーが慣れているとは限りません、そこで必要になってくるのがユーザーの検索行為を助ける役割であるクエリアンダースタンディングです。 query understanding query classification(分類) query correction(訂正) query expansion(拡張) query relaxation(緩和) query suggestion(提案) query segmentation(セグメンテーション) query scoping(スコーピング) 上リストのようにクエリアンダースタンディングは主に7つのコンポーネントから構成されています。 それぞれのコンポーネントの役割を紹介します。 2.1.1 classification(クエリ分類) ユーザーが入力したクエリを分類するコンポーネントになります。 2.1.2 correction(クエリ訂正) ユーザーが入力したクエリの誤字脱字などを訂正するコンポーネントになります。 例えば、googleで「エンジア」と検索すると、「エンジニア」として検索された結果が返ってきます。 ゼロマッチ等の再現率 ※2の低下に対して有効です。 2.1.3 expansion(クエリ拡張) ユーザーが入力したクエリに対してタームを追加するコンポーネントになります。 検索対象のドキュメントを増やすためにクエリを書き換えます。 例えば、「メガネ バイト」に対して、「(メガネ or サングラス) and (バイト or アルバイト)」のようにクエリを拡張することで検索対象のドキュメントを増やすことができます。 2.1.4 relaxation(クエリ緩和) ユーザーが入力したクエリに対してより検索マッチさせるためにクエリを書き換えます。 簡単にできることはストップワードの削除です。 例えば、「バイト 東京都周辺」のようなクエリに対して、 「周辺」を削除してあげることで 検索対象のドキュメントを増やすことができます。 2.1.5 suggestion(クエリ提案) ユーザーが入力したクエリに対して他のクエリを提案するコンポーネントになります。 ここにはクエリオートコンプリートや関連キーワードの表示などがある。 クエリオートコンプリートでは、以下のようにユーザー入力クエリに対して、 以下の例では、enと打つと「エンジニア、園芸」などのような検索クエリを提案しています。 また、関連キーワードもこのコンポーネントの一部です。 ユーザーが再度検索クエリを入力する必要がなくなり、運営目線で言うと離脱を防げる可能性があります。 2.1.6 segmentation(クエリセグメンテーション) ユーザーが入力したクエリに対して、意味的にまとまりのある単語に分割してより意図通りの検索結果を返すようにします。 例えば、New Yorkという単語に対して空白で分割すると全く意味が変わってくるので、分割はせずにNew Yorkという1つの単語としてみなした方が良いでしょう。 2.1.7 scoping(クエリスコーピング) ユーザー入力のクエリやセグメントされたクエリに対して、ジャンルを割り当ててより適合率 ※1を高めることをします。 ユーザーが畜産業のバイトを探しており「豚 飼育 バイト」と検索する例で考えます。 このクエリに対して「豚飼育= 畜産業」のジャンルだと認識することで、豚を飼育している動物園の求人情報も出る可能性があるところ 「畜産業」ジャンルのラベルがついたドキュメントのみ検索結果として表示されるようになります。 以上がクエリプリプロセスを構成するクエリアンダースタンディングの各コンポーネントの紹介でした。 2.2 クエリポストプロセス 検索結果の加工・ランキング等を行うコンポーネントです。 検索結果のランキングではクエリに合致したドキュメントを何かしらの基準で並び替えることをします。 例えば、ドキュメントの登録日順に並べてより新鮮なドキュメントを上位に並び替えたり、機械学習モデルを用いてより複雑な要素を組み合わせてドキュメントを並び替えたり、様々な手法があります。 これ以上はこの記事の範囲を超えるので触れませんが、気になる方はぜひ論文等読んでみてください。 以上のように、一概に検索といっても様々なコンポーネントから構成されていることが分かりました。 スタンバイの検索システムに関しては別記事で詳しく紹介していますので、ぜひご覧ください。 https://techblog.stanby.co.jp/entry/stanby_search ※1 適合率 検索結果の中でクエリにマッチしたドキュメントの割合 ※2 再現率 クエリに対して返すべきドキュメントの中で、どれだけ返せたのかの割合 3 スタンバイにおける検索改善への取り組み ここからは、実際にスタンバイで行っている検索改善取り組みをご紹介します。 主に、前章の内容にそって具体例を載せながら紹介します。 まず、検索周りの大まかなシステム構成は下図のようになっています。 3.1クエリアンダースタンディングの実施 3.1.1 suggest-apiの説明 suggestionを行うapiです。ユーザーの過去検索リクエストからsuggestワードを抽出し、検索エンジンにインデックスしています。 検索窓にユーザーが文字を入力するとその文字にマッチしたsuggestワードを表示しています。 例えば「エンジニア」と入力する最中の「えn」という文字に対してsuggestワードを検索できないといけません。 この場合検索エンジンにsuggestワードをインデックスする際に、「enjinia, enzinia」のようなローマ字読みを追加したドキュメントをインデックスしておきます。 さらにユーザークエリに対しても、「えn => en」のようにローマ字読みに変換します。 その後、変換後のユーザークエリでprefix searchをすることで suggestワードを結果として取得しています。 3.1.2 query-handling-apiの説明 このapiでは、クエリアンダースタンディングの以下コンポーネントを実装しています。 relaxationとexpansionを実現しています。現状主に、ルールベースでクエリアンダースタンディングの各コンポーネントを実現しています。 それぞれの具体例を紹介していきます。 実際のクエリ判定方法は以下の記事を参照してください。 具体的な実装方法などを詳しく紹介しています。 https://techblog.stanby.co.jp/entry/label 3.2.1 relaxation 主にストップワードの削除を行なっています。 例えば、「募集・仕事」のような文字をユーザー入力クエリから削除しています。 スタンバイでは求人情報を扱っているので、「募集・仕事」など明示的にドキュメントに記載する必要がないからです。 これらの単語を削除することにより、 再現率を高めることができています。 expansion スタンバイでは、エンジニア・看護師などのキーワードの他に、東京都・神奈川のように勤務地検索にも対応しています。(検索窓が2つ用意されている) そこで首都圏が入力されたときには、「首都圏 => 東京都、神奈川県」に変換し、or検索にしています。 スタンバイの求人票では具体的な都道府県が記載されていることが多いので、 このような変換処理を入れることで再現率を高めることができています。 機械学習ランキングモデルの説明 スタンバイではYahoo! Japanで独自開発しているSolrのプラグインを利用して、機械学習モデルによるランキングを実現しています。 ランキングモデルの特徴量としては、求人票自体の特徴や検索ログから集計した求人票ごとのクリック数などの実績データも使用しています。 ABテストを重ねて日々モデル改善に取り組んでいます。 まとめ この記事では、スタンバイにおける検索改善の一例を紹介しました。 検索改善と言っても課題に対して様々なアプローチが存在します。 現在スタンバイでは、この記事で紹介した様々なアプローチをとり課題解決に向けて日々取り組んでいます。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 参考文献 検索システム 実務者のための開発改善ガイドブック: https://www.lambdanote.com/products/ir-system-ebook query understanding: https://queryunderstanding.com/ query relaxation: https://queryunderstanding.com/query-relaxation-342bc37ad425 query expansion: https://queryunderstanding.com/query-expansion-2d68d47cf9c8 query segmentation: https://queryunderstanding.com/query-segmentation-2cf860ade503 query correction: https://queryunderstanding.com/spelling-correction-471f71b19880 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
スタンバイでは、様々な技術勉強会やイベントに登壇をしております。 本記事では、これまで登壇した勉強会やイベントでお話した内容や資料をまとめさせていただきました。 スタンバイにおけるECS on FargateからEKS on Fargateへ移行した話/CloudNative Days 2021 by吉田 芳弘 マイクロサービスアーキテクチャな組織、システムにSLOを導入している話/Observability Conference 2022 by小林良太郎 自然言語処理スタートアップに学ぶ実践事例/ML Study #4 by 北川 淳一郎 スタンバイにおけるSLOの、これまでとこれから/【スタンバイ× hey】急成長スタートアップSREのリアル by小林良太郎 スタンバイにおけるECS on FargateからEKS on Fargateへ移行した話/CloudNative Days 2021 by吉田 芳弘 スタンバイでは、2021年5月から求人検索エンジン「スタンバイ」を EKS on Fargateへ移行するプロジェクト を進めてきました。 講演では、移行の計画、EKS on Fargateを選んだ理由、既存システムとの結合、切替方法、今後の展望などについてお話しました。 speakerdeck.com マイクロサービスアーキテクチャな組織、システムにSLOを導入している話/Observability Conference 2022 by小林良太郎 求人検索エンジン『スタンバイ』はマイクロサービスアーキテクチャを採用しており、組織的にも機能毎に分かれて開発・運用をしております。そういった組織にSLOを導入をしていく中で、いくつかの課題が見えてきておりました。 講演では、課題に対して、SRE、開発エンジニアたちはどのように立ち向かったのか。横断的なSLOルールの策定やDatadogを使ったSLO運用環境の統合といった取り組みを、組織・技術面合わせて紹介しました。 SLOのオーナーシップ、SLO違反時の対応フロー、ユーザーの行動に寄り添ったSLOなどを、どのように設計・導入・改善してきたのか。 『100年続く会社ではなく、100回変わる会社』を目指すスタンバイの軌跡を、皆様のSLO導入に関するヒントとしてお使いいただけますと幸いです。 speakerdeck.com 講演内容は下記で視聴可能です。 event.cloudnativedays.jp ※本講演はthinkitへも取り上げていただきました。 thinkit.co.jp 自然言語処理スタートアップに学ぶ実践事例/ML Study #4 by 北川 淳一郎 スタンバイは全国の仕事情報からニーズにあった最適な求人を探すことができる求人検索エンジンであり、 日々、検索精度を向上させるために様々な施策をおこなっています。 講演では、それらの施策のうち、自然言語処理を利用したもの及びこれから利用しようとしているものの紹介をさせていただきました。 最後の質問会では、とても多くの質問をいただき、ご参加いただいた皆さんの自然言語処理への関心が高く窺えました。 speakerdeck.com 講演内容は下記で視聴可能です。 www.youtube.com 検索精度に関する他施策として、ラベル情報での検索については、下記ブログでも紹介しています。 techblog.stanby.co.jp スタンバイにおけるSLOの、これまでとこれから/【スタンバイ× hey】急成長スタートアップSREのリアル by小林良太郎 SREあるあるに対して、スタンバイ社でどのように取り組んでいるのかを発表しました。SLO(Service Level Objective)をどのように考え、取り組んでいるのか、デプロイに関するツールや課題など、日々の開発にいかせる観点でお話しさせていただきました。 speakerdeck.com スタンバイでは、求人検索エンジンを開発運用しているからこそ、またスタンバイだからこそ発信できる内容を、積極的に勉強会やセミナーを通して、発信してまいります。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
(本記事は、執筆時2022年7月時点の情報です) 株式会社スタンバイは、日本最大級の求人検索エンジン「スタンバイ」の開発・運営を手掛けています。 “UPDATE WORKSTYLES 「はたらく」にもっと彩を”をミッションに掲げ、テクノロジーの力で世の中の「はたらく」をアップデートしていくことを目指す弊社ですが、どんな技術を用いてどこを目指しているのか。 本記事では、元ヤフーCTOとして日本の検索/広告技術を牽引、2021年1月より弊社CTOを務める明石信之より、その全容について語っていただきました。 【profile】 株式会社スタンバイ CTO 明石 信之 システム開発会社を経て、2000年にヤフー株式会社入社。Yahoo! JAPANの広告配信システムなどの開発に従事。2009年に同社CTOに、2013年にシニアフェローに就任。2014年に株式会社フリークアウト執行役員CTO就任。IPO後を見据えたエンジニアリング組織作りに携わる。2017年1月、フリークアウト・ホールディングス執行役員就任。その後、ユアマイスター、インフォステラ、フリークルなどベンチャー企業の技術顧問就任。2019年10月、東京都チーフデジタルサービスフェロー委嘱し、東京都のDX推進および構造改革のためのアドバイザーを務めている。2021年1月スタンバイCTO就任。 ヤフー元CTOが挑む新たな検索エンジンづくり ーまずはじめに「スタンバイ」について教えてください。 スタンバイは、求人(仕事探し)に特化した検索エンジンです。 求人企業が良い人材の採用が出来ること、求職者がより良い企業に就職出来ることを最終目的としており、その課題を技術を用いて解決しようとしています。 主な技術としては、検索技術/広告技術を活用しています。 広告は2000年、検索は2008年からと私自身もう20年近く携わっている領域ですが、この間の進化も目覚ましく、いまだに新しい発見があるなど、非常に奥が深く一筋縄でいかない面白い領域ですね。 ーこれまで明石さんが関わられてきた検索エンジンと、スタンバイはどんな違いがあるのですか? 検索エンジンとは、ユーザーの意図を汲み取り、複数のドキュメントをランキング化したものを提供するサービスです。言葉にすると簡単ですがこれを実現することが、難しくもあり楽しい世界です。 ユーザー意図を汲み取るにしても、ユーザー自身が明確な意図を持たれていないケースもあるので、サービスに蓄積されたログによる統計やクラスタリングやユーザの属性などを利用して、検索文字予測(インテント)など周辺技術を活用しながらユーザー意図をより正確に導きだす必要があります。 ランキングにおいても、どんな順位でランキングするのが最適なのかという問いに答えはありません。一番オーソドックスな方法としては対象ドキュメントに含まれるターム数でランキングを出すものですが、それだけでは不十分なことも多く、機械学習を始めとする様々な技術を組み合わせてKPI沿った予測精度の改善を行っていることが多いですね。 そんな中でも、スタンバイが取り組んでいる求人検索は、特有の難しさがあると考えています。 例えば、通常のWEB検索などではユーザーのほとんどの検索は探したいものが明確で、的確なキーワードを入れて検索いただけることが多いです。しかし求人検索では、ユーザー自身が「働きたい」というニーズはあるものの、どんな仕事を探しているか不鮮明なことが多く、検索をしながらより多くの検索結果をみて探す傾向があります。例として「正社員」や「都道府県名」のみといったビックワードでの検索では、何を優先してランキングするか、検索結果だけでは補えないユーザーの意図をどのような技術で導くか非常に難しい問題だと考えています。 また、数字や記号など検索エンジンで当てづらい主観的なキーワードやストップワードの検索が非常に多いのも、求人検索の特徴です。EC検索でも”かわいい” “おしゃれ”などの主観的なキーワード、商品番号、型番などのストップワードは多いのですが、何を買いたいかのニーズを絞りやすいので予測しやすく精度を高めやすいですが、求人検索はこれに加えて就業先など位置情報などさまざまな条件を複合的に加味して解決しないものも多くあります。 そのため、通常の全文技術だけでは解決しないことも多く、後でもお話しますが、様々な技術を利用して、より求人を理解し、よりユーザのニーズに応えられるよう、最適な方法を日々考えながら改善に取り組む必要があるのが特徴ですね。 ー非常に奥が深い検索エンジンづくりですが、スタンバイで取り組んできたことについて簡単に教えてください。 今までやってきたことを順にお話をすると、オーソドックスに検索キーワードに対して全文検索エンジンを用い、いかにCTR最適化を行いランキングしそれらを継続的に向上させ、提供しつづけるいうことに注力しました。これは閲覧(クリック)されやすい求人はユーザニーズに合致しているであろうという仮説を前提にした取り組みであり、社内での改善サイクルの確立と全文検索エンジンのknowledgeの蓄積、KPIであるCTR改善においても一定の成果をあげることができました。 しかし、全文検索エンジンだけのon the flyで適切な検索結果を返すことの改善に限界が見えてきたため、次の取り組みとして求人票判定機の開発を開始しました。これは、検索エンジンにインデックスする前に、求人票を判定機にかけて、その求人がどのような求人なのかを事前にラベル付けするものです。 例えばコンビニ店員の仕事を探すために〈コンビニ〉で検索をすると、通常の全文検索だけではコンビニ店員の求人も、コンビニが近くにある施設ではたらく求人も一緒に出てきてしまいます。それらを事前に求人票判定器を通すことにより、コンビニ店員の求人なのか、コンビニが近くの異なる求人なのかを判定器を通してラベル付けすることで、よりユーザーが求めている結果を表示することができます。 実は、検索結果のCTRのみをKPIとして追求すると、検索結果に必要最低限の情報のみを表示して詳細を見ないとわからないようにすることで改善することはたやすいのですが、私たちの目的は最初に説明したとおり、求人企業が良い人材の採用が出来ること、求職者がより良い企業に就職出来ることです。ですので、ユーザのニーズにあった求人票で且つより質の良い求人票というものを追求しながらランキングできるように日々改善を続けています。 検索エンジンづくりの先に、見据える世界とは?「検索させない検索エンジン」 ー奥深い検索エンジンづくりですが、検索技術の現状についても教えてください ひと昔前に比べて便利なソフトウェアが数多く生まれ、検索エンジンは多くの方が触れる身近な技術となりました。広告技術や推薦技術など現在でも活用される技術も検索で培ったknowledgeやエンジンなどの技術から派生しており、WEB業界に大きく寄与している技術領域といえるでしょう。 簡単に検索機能をサービスに投入するサービスが増えた一方で、検索を主要技術として真面目に開発に取り組んでいる企業は少ないと感じています。実際にビックテック以外で検索技術の開発に取り組んでいる企業は稀少な存在であり、検索技術の発展が一部会社とアカデミックに閉じられてしまっていますが、我々は、まさにそこに対して事業の成長という成果とともに挑戦していきたいと考えています。 私たちの技術はいわゆるビックテックと言われる企業から比べると大きくビハインドしていますが、スタンバイが取り組む求人検索は、現時点では非常にローカルに根付いた発展途上な領域です。競合含め求人検索エンジンでの明確な正解に辿り着いているプレイヤーはいないと考えているので、より挑戦しがいがありますね。 ー今後のスタンバイの展望はどう考えているのでしょうか? 現在は、求人の閲覧に対して最適化を行っていますが、今後、求人への応募や、果ては就職や採用というところまでこの技術を昇華させていきたいと考えています。 そのために、現在はユーザーニーズをより深めるための検索機能を多角的に開発しています。 今後検索エンジンのデータが溜まっていけば、ユーザーのデモグラフィックの情報を元に、よりユーザーに適した求人を表示することが可能となります。検索しているユーザーの環境を把握し、より合致している求人を提案する取り組みなど実施していきたいですね。これらの精度が上がれば、アプリのプッシュ通知などにも使えるようになります。これらはIR(Infomation Retrieval)の中でも推薦技術の領域となりますが、それらを活用しながら、よりユーザーのニーズにより合致する検索エンジンに仕立て上げていきたいです。 ただ、前述の通り、求人検索においてはユーザー自身、ニーズが曖昧なケースも多いため、今のような検索窓を置きユーザーに検索いただくモデルでは、本質的な課題解決にはならないと考えています。 将来的には、ユーザーによる検索ではなく、こちらからユーザーの意図を汲み取り、求人を提案できるサービスになることが必要です。ここまでユーザーの検索に対してより適した結果を出すかという取り組みについて話してきましたが、まさに目指すゴールは「検索させない検索エンジン」ですね。 今は正直まだやりたいことの半分も実現出来ているわけではないですが、一つずつ着実に階段を登っています。やればやるほど成果に繋がる実感は持てているので、まさに一番楽しい時期ですね。 ー最後にこれを読んでくださる方に一言お願いします。 色々なお話をしましたが、実際には、日々地道な技術の積み重ねと挑戦の繰り返しです。 スタンバイで取り組んだ技術の挑戦については、社内に閉じずに積極的に外に多く発信をしていきたいと考えています。検索技術や広告技術に興味がある方と繋がりたいと思っていますし、発信や交流などをして、より技術力を高めていきたいと思っています。 ーStanby Tech Blogにも通じますね。少しずつではありますが、今後もスタンバイの取り組みをブログを通じて発信していきたいと思います。本日はありがとうございました。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
MLOps導入でAmazon SageMaker PipelineによりMLワークフロー構築の話 はじめに はじめまして、スタンバイのSearchAdvertisingCoreGroup(検索・広告コアグループ、以降SACG)で機械学習関連の開発をやっている王です。今回はAmazon SageMaker PipelineでMLワークフローを構築する取り組みを紹介します。 MLOpsとは 私が所属しているSACGは機械学習モデルを用いて改善施策をオフラインで効果検証して、A/Bテストで仮説を確かめることでユーザーの検索体験を継続的に改善しています。 従来Group内ではAWSのマネージド機械学習基盤Amazon SageMakerを利用してPoC(Proof of Concept)、機械学習モデルの構築、トレーニングなどを行っていました。 過去PoCの結果を振り返りにくい、もしメンバーが居なくなった際の引き継ぎなど問題がありました。 このような理由から、MLOpsの導入検討を始めました。 MLOpsについて明確な定義はなくて、DevOpsからの派生で機械学習のワークフローを効率化できることを目指しています(個人的見解)。よく参考にされるのはGoogleがプロセスの自動化のレベル別でMLワークフローを定義しています. - MLOps レベル 0: 手動プロセス - MLパイプラインは自動化を含まない - MLOps レベル 1: MLパイプラインの自動化 - MLパイプライン自体を自動化する - MLOps レベル 2: CI/CDパイプラインの自動化 - MLパイプライン自体及びCI/CDを自動化する 出典: MLOps: 機械学習における継続的デリバリーと自動化のパイプライン 上記ワークフローを構築するために、本番適用の時手間がかかる、実験管理しにくいなどの課題が出て、MLOpsはこのような課題を解決することが期待されています。 Group内で存在する課題を整理して、優先度をつけて段階的にレベルアップを目指しています。私達は、MLOps導入を最初の推進ゴールとして「MLOps レベル 1」を部分的に実現すること、具体的には「データの前処理、モデルトレーニング、モデル評価をパイプライン化する」を進めていきたいと考えています。 Group内の課題整理 MLOpsを導入する前に、Group内で実際にモデリングを担当しているメンバーにヒアリングして出てきた機械学習プロジェクトを進める中でよくある課題を紹介します。 後で実験が再現しにくい 「先月のABテストをした時の、その施策結果を今のデータにもう1回確認したい」、「その時のモデルの前処理はどんな処理でしだっけ?」などの要望がよくあります。ABテスト終了の数ヶ月後に実験を再現したい時に、当初の学習、前処理ソースコード、学習時と評価時に使うデータ、モデルのハイパーパラメータなどを全部用意しないと再現しにくいです。再現できない時に過去の知恵を活用できなくなって機会損失になりました。 モデル生成の手間 MLOpsを導入する前に、ABテストを行う際にモデルを作成にあたって、データ取り込む、データ前処理、モデル学習、モデル評価を全部手動で実行する必要がありました。特に頻繁にABテストを回す時に、このような手順の手動実行は時間もかかるし、ヒューマンエラーも起きやすい状態でした。 またモデル開発を担当するメンバーが用事で休みになった時、他のメンバーに引き継ぎも上記のプロセスを実行するときコードやパラメータなどを全部伝わないといけないので、引き継ぎもしにくい状態でした。 MLパイプラインの構築 MLOps導入最初の推進ゴールに当たって、Group内整理した課題「後で実験が再現しにくい」「モデル生成の手間」に対して、実験管理できる機械学習パイプラインの導入を考えています。 今回はGroup内利用する機械学習プラットフォームAmazon SageMakerとの連携や実験管理をライトに始められることから、Amazon Sageaker Pipelineの導入を決定しました。 Amazon Sagemaker Pipelineとは、機械学習ワークフローを管理するCI/CDサービスです。機械学習プロセスの各ステップをワークフローに組み込んで、JSON形式のDAG(Directed Acyclic Graph)としてワークフローを定義して管理や再利用できます。ワークフローを実行するときに全てのステップの詳細をログに記録して後から自動追跡できます。 CTR予測モデルを例にして、ワークフローの流れを簡単に紹介します。全体の構成は下記の図のイメージとなります。 CodeBuildから対象のpipelineを実行します。まず「SageMaker Processing Job」を起動してS3に置くデータに対する前処理を行います。処理されたデータを学習評価用のデータとしてS3に保存します。 次に「SageMaker Training Job」を起動して、前処理されたデータを用いてモデルを学習します。学習されたモデルをS3に置きます。最後に「SageMaker Processing Job」を起動して、テストデータとモデルを使ってモデルを評価して、オフライン評価結果をS3に保存します。 実行の詳細はSageMaker Studioで確認することできます。各ステップをダブルクリックするとステップの入力、出力、パラメータなどの情報を確認できます。 導入の効果としては、 - 過去の実行結果およびパラメータ、データなどを追跡できて、再実行が可能になる - データ収集からモデル評価まで1つのパイプラインでまとめてOne-Clickで実行できる まとめ この記事では、Group内で取り込んだMLOps導入の経緯、施策を紹介しました。MLOps導入によって機械学習プロジェクトの運用を自動化できて、手間を省き、実験に多くの時間を割くことができるようになりました。今後も機械学習施策をどんどん検証してユーザーに価値を提供できるように仮説検証のサイクルを回していきたいと考えています。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
求人検索エンジンで使用するラベル付与の話 はじめに スタンバイでは求人検索エンジンにラベル情報での検索を可能にしています。 ラベルとは求人情報や検索キーワードの特徴的な情報に対するTag付けと考えていただければイメージしやすいかと。 本記事ではRuleによるラベル付けをテーマとしています。 ラベルの使い所 例として「住吉」という駅の求人を検索する場合を挙げます。住吉という駅は全国に下記の数存在します。 東京都 住吉駅 大阪府 住吉駅 熊本県 住吉駅 長崎県 住吉駅 兵庫県 住吉駅(JR西日本) 兵庫県 住吉駅(阪神電鉄) 「住吉駅」という単語のみで検索する際は上記全ての駅の求人データが対象となりますが、「半蔵門線 住吉駅」の場合は「半蔵門線」は東京にある路線なので、1の「東京都 住吉駅」のみが対象となって欲しいところです。しかし、「東京都 住吉」の求人データに「半蔵門線」の記述がない場合には絞り込むことができません。そこで駅名周辺情報から同一駅名でも別物と扱える仕組みとしてラベルを導入しています。 複数の同一駅名から絞り込む情報として、 都道府県 、 路線名 を使用しています。東京都、大阪府の住吉駅であれば以下の絞り込み情報との組み合わせが挙げられます。 このように同じ「住吉駅」であっても周辺情報を活用し、ラベル付与すれば区別できます。 区別できればこのラベル情報を用いて、より意図に近いものを検索することが可能になります。 ラベル付けを行うにはMLかRuleか ラベル付けを行うにあたり考えられる手法は大きく分けてRuleベースと機械学習の2つが挙げられます。 2つの手法のメリット・デメリットをまとめると以下ようになります。 メリット デメリット Ruleベース 高速 学習データ作成不要 Ruleで表現できないものは対応不可 機械学習 Ruleで表現できないものに対応可能 学習データ作成負荷が高い どちらを採用するかというとHybridな形で行うのがBetterかと考えます。 単語の有無で決定できるようなラベル付けまで機械学習を使う必要はありません。 しかし、Ruleで表現できないものもあるので機械学習を使用しないという選択肢もありません。 まずはRuleベースでラベル付けを実施、その後まかない切れない部分を機械学習で補う形としています。 Ruleベースの機能定義 Ruleベースには下記の5つの機能を実装しました。 単語の有無Rule機能 指定単語の有無によりラベルを付与、削除する 単語の組み合わせRule機能 2つ以上の単語の組み合わせの有無によりラベルを付与、削除する ラベルと単語の組み合わせRule機能 付与したラベルと単語の組み合わせによりラベルを付与、削除する Rule適用順序機能 Ruleの適用に順序を設け、ラベルを付与する順序を制御する 例: 順序1. 東京 に対して 駅 ラベルを付与 順序2. 東京都 の場合には 駅 ラベルを削除 ラベル付与先の指定機能 ラベルを付与する形態素を制御する 例: 都営新宿線 住吉駅 の 住吉駅 にのみラベルを付与する場合に使用 都営新宿線 , 住吉駅 ともにRule条件だが、駅ラベルを付与する場合には路線名には付与したくない場合がある 構成 開発言語 : Rust 構成Module : 形態素解析 Rust製の Lindera を使用しています。社内にLinderaのMaintainerがいることもあり、採用しました。 DoubleArray RuleEngine DoubleArray Ruleを検索するのにDoubleArrayを使用しています。DoubleArrayの詳しい説明はここでは割愛します。 DoubleArray の概要はこちらのページが簡潔にまとまっていて理解しやすいです。 取り立てて特別なことはしていませんが、DoubleArrayの検索状態を維持しながら検索を行えるようにしています。 例としては下記になります。 DoubleArrayに追加する情報 ・国際フォーラム ・国際展示場 検索する文字列 ・国際展示場駅 検索時の形態素解析された[国際 展示 場 駅]で、下記のように文字列を作成して検索するのは非効率 ・国際 ・国際展示 ・国際展示場 ・国際展示場駅 DoubleArrayのbase、check、tailの状態を保持しておけば前回の検索結果の続きから検索することが可能 RuleEngine 上記の「住吉駅」のRuleをjsonで表現すると下記のようになります。 1001 : 東京都の住吉駅 1002 : 大阪府の住吉駅 {"order":1, "add_label":[[1, [1001, 1002]]], "word_rule":[{"word":"住吉駅"}]} {"order":2, "del_label":[[1, [1002]]], "word_rule":[{"word":"東京都"}, {"word":"住吉駅"}]} {"order":2, "del_label":[[1, [1002]]], "word_rule":[{"word":"新宿線"}, {"word":"住吉駅"}]} {"order":2, "del_label":[[1, [1002]]], "word_rule":[{"word":"半蔵門線"}, {"word":"住吉駅"}]} {"order":2, "del_label":[[1, [1001]]], "word_rule":[{"word":"大阪府"}, {"word":"住吉駅"}]} {"order":2, "del_label":[[1, [1001]]], "word_rule":[{"word":"上町線"}, {"word":"住吉駅"}]} {"order":2, "del_label":[[1, [1001]]], "word_rule":[{"word":"阪堺線"}, {"word":"住吉駅"}]} jsonの各項目は以下の内容を表しています。 order : Ruleの適用順を表す。小さい値のRuleから順にRuleを適用する。 add_label : ラベルを付与するword_rule上の形態素Indexとそのラベル名。 del_label : ラベルを削除するword_rule上の形態素Indexとそのラベル名。 word_rule : 構成要素の「word」と「labels」が解析文章と一致すれば、「add_label」, 「del_label」を適用する。 word : 形態素表記と同一の文字列が解析文章に存在すれば一致とする。 複数ある場合は、記述されいてる順序もRule条件となる。 上記の "word_rule":[{"word":"東京都"}, {"word":"住吉駅"}] では、以下のようになる。 解析文章:「東京都の住吉駅」 Rule一致 解析文章:「住吉駅 東京都」 Rule不一致 labels : 形態素に付与されたラベルが解析文章に存在すれば一致とする。 配列となっておりラベルが複数記述されている場合はAND条件として処理する。 上記のRuleに対して 「半蔵門線の住吉駅」 を解析するとRule適用は以下の流れで行われます。 order:1のRuleが適用され下記の状態となる 半蔵門線 [] の [] 住吉 [1001, 1002] 駅 [1001, 1002] order:2のRuleが適用され下記の結果となる 半蔵門線 [] の [] 住吉 [1001] 駅 [1001] Rule検索の全体の流れとしては下記のイメージとなります。 まとめ Ruleベースを導入することで単純なものならば機械学習を導入しなくとも事足りることを紹介しました。 機械学習は手法の一つであって目的ではないので、最適な手法を採用することが重要と考えます。 スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com