ECS インフラの変遷

技術部 SRE グループの鈴木 (id:eagletmt) です。
クックパッドでは Amazon ECS をオーケストレータとして Docker を利用しています。Docker 自体は2014年末から本番環境にも導入を始めていましたが当時はまだ ECS が GA になっておらず、別のしくみを作って運用していました。2015年4月に GA となった ECS の検討と準備を始め、2016年より本格導入へと至りました。クックパッドでは当初から Hako というツールを用いて ECS を利用しており、Hako の最初のコミットは2015年9月でした。
https://github.com/eagletmt/hako/commit/7f95497505ef78491f3f68e9d648204c7c9bb5e2
当時は ECS に機能が足りずに自前で工夫していた部分も多かったのですが、ECS やその周辺サービスのアップデートにより不要になったものもいくつかあります。今回はその中からいくつか紹介しようと思います。

クレデンシャルを環境変数に設定する

データベースに接続するときのパスワードのように、アプリケーションコードにハードコードすべきではなく、安全に保存しなければならないクレデンシャルが存在します。そういった値を扱うために、クックパッドではクレデンシャルを Vault に保存し、Hako の定義ファイル上で Vault に保存された値を参照する記法を導入し、デプロイ時には Hako が Vault から値を取得して ECS のタスク定義に注入するようにしました。 f:id:eagletmt:20210805113108j:plain このようにすることでクレデンシャルが Git リポジトリ内に入ることを防ぎ、また ECS のタスク定義を閲覧する権限を絞ることでクレデンシャルに対する権限管理を行うようにしました。

このしくみは長いこと運用されてきており今もまだ残っていますが、現在では ECS のアップデートによりクレデンシャルを与えるための機能が追加されたので、それを利用するようになっています。
https://docs.aws.amazon.com/AmazonECS/latest/userguide/specifying-sensitive-data.html
これにより Parameter Store や Secrets Manager、KMS によってクレデンシャルの安全な保存と権限管理を達成でき、タスク定義の閲覧を制限する必要がなくなったり、Vault を自前で運用したり Hako のようなツールで工夫する必要もなくなりました。

1つのロードバランサーを複数のアプリで共有する

ECS には ELB と連携する機能が当初からありましたが、1つの ECS サービスに対して1つのロードバランサーしか関連付けることができませんでした。つまり1つのアプリに対して1つのロードバランサーが必要でした。これは機能的には問題無いのですが、ELB のロードバランサーには数に比例した料金が設定されており、どんなに利用が少なくても一定の料金がかかります。公開されている Web アプリの場合は24時間アクセスがきますが、社員が時々使うだけのようなスタッフ向けアプリの場合はロードバランサーの分の料金の割合が高くなります。そのため、できるだけロードバランサーを共有して料金を抑えたいという事情がありました。一方でロードバランサーを共有するとロードバランサー毎のログやメトリクスが複数のアプリで混ざったものになってしまう問題があるため、これを許容できるようなワークロードに限ってロードバランサーを共有することにしました。

そこでロードバランサーを共有するケースでは ECS と ELB の連携機能を使わず、独自にサービスディスカバリを実装し1つのロードバランサーから複数のスタッフ向けアプリにプロキシするようなしくみを作りました。1つのロードバランサーから EC2 上に起動した nginx にプロキシし、その nginx の設定は consul-template から生成されるようにしてホスト名に応じて各アプリにプロキシされるようにしています。 f:id:eagletmt:20210805113305j:plain このしくみのもう少し詳しい説明は https://speakerdeck.com/eagletmt/ecs-woli-yong-sitadepuroihuan-jing?slide=28 にあります。

以前はこのような工夫でロードバランサーの料金を抑えていたのですが、2016年に ALB が登場し、2019年に ALB が Host ヘッダによってルーティングを変更できるようになったことで状況が変わりました。これにより ECS サービスに対応するターゲットグループを1つのロードバランサーに複数関連付け、Host ヘッダの値でルールを作成することで、ロードバランサーを複数アプリで共有することができます。 f:id:eagletmt:20210805113432j:plain このようなしくみにすることですべて AWS の機能で済ませることができるようになり、Consul を運用したり各アプリにプロキシする nginx を運用したりする必要がなくなりました。

gRPC サーバを ECS で動かす

クックパッドではマイクロサービス化の過程で gRPC を導入し、Ruby や Go 等で実装された gRPC サーバが動いています。gRPC サーバを動かし始めた当時は ALB がサポートしていなかったため、gRPC サーバと通信するためのしくみを自前で作る必要がありました。ここで必要なものは gRPC サーバのサービスディスカバリとプロキシで、前述のロードバランサーを共有したい状況と似ています。しかしこの時点で社内にサービスメッシュが整備されていたため、gRPC のプロキシには Envoy を利用できそうでした。gRPC サーバ向けに Envoy の SDS API *1 を実装すればサービスディスカバリについても実現できそうだったので、そのようなしくみを作って gRPC サーバを ECS で動かすようにしました。また、非 gRPC のサーバではエラーレートやレイテンシといった基本的なメトリクスが ALB に依存していたので、gRPC サーバの手前にも Envoy を置くことで Prometheus にメトリクスを入れることにしました。 f:id:eagletmt:20210805113504j:plain このしくみの詳細は https://techlife.cookpad.com/entry/2018/05/08/080000https://logmi.jp/tech/articles/320715 を参照してください。

このように gRPC サーバを ECS で動かすには工夫が必要だったのですが、2020年に ALB が gRPC をサポートしたことにより独自のしくみ無しで gRPC サーバを動かせるようになりました。現時点で ALB では gRPC レベルのステータスコードをメトリクスからもログからも確認することができないため、間に Envoy を置いてメトリクスやログをとっている点は変わってませんが、https://github.com/cookpad/sds やそれに登録するためのしくみを運用することなく gRPC サーバを ECS で動かせるようになっています。 f:id:eagletmt:20210805113614j:plain

まとめ

ここまでで紹介したもの以外にも、コンテナインスタンスの drain がサポートされたことにより ECS クラスタのスケールインが容易になったり、タスク内のコンテナ間に依存関係を定義できるようになったことでサイドカーコンテナのヘルスチェックが通るまでメインのコンテナの起動を遅らせることができるようになったり、daemon scheduling strategy でデプロイしたタスクが drain 対象になったときに最後に停止されるようになったことでたとえば daemon scheduling strategy でデプロイしている cAdvisor で最後までメトリクスをとれるようになったりと、ECS が GA になった当時と比べるととても使いやすくなっています。

このようにクックパッドでは AWS を活用しつつも自分たちのニーズに合わせてしくみを自作し、社内の基盤を構築してきました。そして AWS のアップデートにより自作のしくみが不要になったときは積極的に自作を廃止し、できるだけ AWS の機能だけで済むようにしてきました。自分たちの目的のために必要であれば自作できることも大切ですが、メンテナンスや運用の手間をできるだけ減らすために、目的を達成できる範囲内でできるだけ自作を減らすことも大切だと考えています。我々 SRE グループはそのようなバランス感覚を持ちながら必要なものを開発していける仲間を募集しています。
https://cookpad.jobs

*1:v1 当時の名前。現在では v3 EDS API になっている

大規模配信に耐える広告新商品「材料ジャック」の設計と開発

こんにちは! メディアプロダクト開発部の名渡山 ( @pndcat ) です。

業務では広告システム全般の新規開発・保守・運用を担当しています。 本稿では、クックパッドが企業向けに販売している広告商品の開発について紹介します。

クックパッドの広告プラットフォーム

クックパッドの広告は、ネットワーク広告と、自社でシステムを開発し、企業が枠を一定期間買い取り掲載をする 純広告 があります。 過去に Header Bidding 導入によるネットワーク広告改善の開発事情Prebid.js 導入による Header Bidding 改善の舞台裏 などでネットワーク広告に関する投稿があったのですが、今回はクックパッド純広告について紹介します。

クックパッド純広告の商品は多岐にわたります。 企業の商品紹介ページに遷移する通常のバナー広告はもちろん、商品を使ったレシピコンテストなど、クックパッドならではのメニューを揃えています。 中でも人気のある商品が「カテゴリジャックバナー」です。 これはクックパッドで特定のキーワードが検索されたとき、ページ内の広告すべてを1社独占 (ジャック) で表示するものです。

f:id:marin72_com:20210730140635p:plain:w170

材料ジャックとは

2019年に、純広告の商品として「材料ジャック」を開発しました。 材料ジャックは、ターゲティングしたい材料が使われているレシピページに広告をジャック配信する商品です。 例えば、お酢の広告を出したい場合でも、「お酢」というキーワードで検索するユーザーは少ないので、先述のカテゴリジャックバナーは不向きです。 しかし、検索キーワードではなく、レシピの材料に連動する材料ジャックならば、「お酢」が使われる様々な種類のレシピで広告を出すことができます。 調味料など検索頻度が少ないキーワードに対してターゲティングをしたいニーズに応えた商品が材料ジャックです。

f:id:marin72_com:20210730140807p:plain:w170

基本的な入稿と配信について

材料ジャックの設計に踏み込む前に、クックパッドの広告配信システムの概観について説明します。 ユーザに広告を表示するまでに必要なものは、広告の入稿と、広告の配信の2つがあります。

入稿

クックパッドではバナー広告やカテゴリジャックバナーの配信を行うために、入稿担当者が商品名や画像、リンク先、ターゲティング情報などを入稿します。 入稿用のアプリがこの情報をクライアント (Webブラウザなど) 用の JSON に変換し、配信 DB に保存します。

f:id:marin72_com:20210730001506p:plain:w500

配信

配信サーバーはクライアント (Web/Android/iPhone) から HTTP リクエストを受け、検索キーワードなどのターゲティングを考慮しながら広告を抽選します。 抽選された広告の JSON をクライアントに返し、クライアントが JSON からビューを組み立て、広告を画面に表示する、という仕組みになっています。

f:id:marin72_com:20210730001726p:plain:w300

材料ジャックの課題

冒頭でも述べましたが、材料ジャックは、レシピページの材料欄にターゲティングしたい材料があるときに広告を表示する商品です。 言葉にすると簡単そうですが、実際には

  • 既存の広告入稿・配信の仕組みを変更しない
  • 材料名の表記ゆれに対応する
  • 入稿者の作業内容を大幅に増やさないようにする
  • 既存の広告のレイテンシを保ったまま、材料ジャックも配信する
  • 材料ジャックの障害が起きたときに、通常の広告配信には影響が出ないようにすぐに切り戻しができる

といった大きな課題がいくつもありました。 また、今回は設計から実装まで1ヶ月の短期スケジュールでの爆速開発が求められたため、手戻りが起きないようにきちんと設計をする必要がありました。 これらの課題をどうやって解決したのかを紹介します。

入稿実装

材料名の表記ゆれへの対応

既存のカテゴリジャックでは「ジャックしたいキーワードが、検索キーワードに含まれている時広告を表示する」というルールベースの広告です。 例えば、きんぴらに対するカテゴリジャックを行う際は「きんぴら」「キンピラ」「金平」のキーワードを登録します。

材料ジャックは、レシピに書いてある材料を元に広告を表示するので、カテゴリジャックと比べてはるかに多様な揺れに対応できる必要がありました。 例えば、塩の場合は、「しお」や、漢字の「塩」や、別表記の「食塩」、記号が含まれている「★塩」など、250種類以上のパターンが存在します。 カテゴリジャックで用いられたようなルールベースの方法を用いると、さまざまなパターンの材料名で登録する必要があり、非常に手間が大きく困難な作業になります。

表記ゆれの対応をするために最初に思い浮かんだことは、材料名の中から記号を取り除くデータクレンジングを行い、形態素解析ツールである Mecab を使う手法でした。 しかし、機械学習グループに相談したところ、機械学習グループが開発した材料名正規化テーブル を使って名寄せすることを提案してもらいました。

材料名正規化テーブル

材料名正規化テーブルでは、正規化後の材料名、ユーザが入力する材料名、正規化後の材料名に対して一意に定まるID (concept_id) の3つのカラムがあります。 このテーブルを使うことで、レシピの材料名から正規化後の材料名と concept_id を取得することができます。 正規化の精度*1 は70%〜ですが、調味料はほぼ100%になります。 よく使われる材料名はたくさんのパターンの材料名が登録されており、例えば塩に対応する材料名は251行存在します。

正規化後の材料名 ユーザ入力する材料名 concept_id
100
しお 100
食塩 100
★塩 100
材料名正規化テーブルを使った材料ジャック入稿

入稿時に材料名の表記ゆれパターンを網羅させるのは現実的ではないので、材料名正規化テーブルを採用しました。 材料名正規化テーブルを使うことで入構の手間を減らし、材料のカバー率も上げることができました。

広告 登録するジャック対象 ターゲティングするデータ例
カテゴリジャック 検索キーワード 塩(完全一致でカテゴリジャックが発動)
材料ジャック 材料の concept_id 100 (100に対応する「塩」「しお」など多くの表現で材料ジャックが発動)

最終的な材料ジャックの入稿実装

入稿の全体図はこのようになりました。 配信 DB には、広告の JSON を書き込みます。 配信キャッシュストアには、材料名をキー、concept_id を値とした材料名正規化テーブル(2万件)をまるごと書き込みます。 というのも、材料名正規化テーブルは Redshift 上にありオンラインでクエリをすることができないためです。 また、詳細は後述しますが、広告の抽選を高速化するためという狙いもありました。

f:id:marin72_com:20210730001749p:plain:w500

配信実装

先述したとおり、ターゲティング条件として登録されているのは材料名そのものではなく、材料の concept_id です。 広告の配信サーバーにおいては、いま表示しているレシピに含まれている材料名が材料ジャックを発動させるかを判定するために、広告を抽選する前に材料名を concept_id に変換する必要があります。 そこで、配信サーバーは先ほど配信用キャッシュストアに入れておいた材料名正規化テーブルを使って材料名から concept_id を引きます。こうすることで、DB を介することなく高速に材料名と concept_id の変換を行えます。 concept_id を求めたあとは、通常の広告の配信と同じフローになります。

f:id:marin72_com:20210730002011p:plain:w400

さらにキャッシュストアを活用した実装

材料名から concept_id をキャッシュストアから引くこと以外にも、

  • recipe_id から concept_ids (concept_id の配列 = 材料名の配列) を引く
  • recipe_id を key に、concept_ids を値にキャッシュする

処理を加えました。 この2つの工程を追加することで、さらにキャッシュを活用し高速に concept_id を求めることができました。

recipe_id を key に、concept_ids を値に持つようなテーブルとは、以下のような表です。

key (recipe_id) value (concept_ids) 含まれている材料
1 [100, 200, 300] 塩、豚バラ、サラダ油
2 [] 材料がない、もしくは、材料名正規化テーブルにはない表記の材料名のみ使われている

事前にすべての recipe_id の concepte_ids をキャッシュすることができればよいのですが、cookpad 全レシピ (350万以上) の concept_ids をキャッシュするのは現実的ではないため、アクセスがあったときにキャッシュをすることにしました *2

初回アクセス

配信サーバーはキャッシュストアに recipe_id から concept_ids を引きにいきますが、初回アクセスではキャッシュされていないため concept_ids は返ってきません (図: 赤線部)。 次に、材料名で concept_id を引きます。 次回以降 recipe_id から concept_id で引けるように、recipe_id をキー、concept_ids を値としたハッシュをキャッシュしておきます (図: 青線部)。 レシピに concept_id が一つも含まれていない場合は、空配列をキャッシュします。 最後は配信 DB に広告をリクエストし広告 JSON を受け取り、クライアントに返します。

2回目以降のアクセス

配信サーバーからキャッシュストアに recipe_id を投げると concept_ids が返ってきます (図:赤線部) 。 材料名を一つひとつ concept_id に変換することなく、すぐに配信サーバーは広告を抽選することができます。 また、recipe_id を投げて空配列が返ってくる際は、レシピに concept_id が含まれていないと判断できるため、空配列をキャッシュさせるのも重要です。 これらの工夫によって、材料ジャック広告追加後もレイテンシをキープすることに成功しました。

f:id:marin72_com:20210730002038p:plain:w400

材料ジャックの入稿から配信のすべての実装

こちらが入稿から配信までの全容になります。 いろいろな課題があった材料ジャックですが、材料名正規化テーブルの使用やキャッシュストアの利用により、レイテンシを保ちつつ、既存実装を変更なく追加実装のみで実現することができました。

f:id:marin72_com:20210730002112p:plain:w600

設計で考慮したこと

入稿・配信の実装を書きましたが、最後に設計時に考慮したことを紹介します。

使用するデータやミドルウェアの事前調査

まず、配信サーバーが使っている ELB と Nginx のヘッダーサイズの制限と現在の使用量を確認しました。 ELB の制限は 16KB、Nginx の制限は 8KB で、現在のヘッダ使用量は 4〜5 KB であるため、材料ジャックにおいては最大 1KB 程度使用しても問題ないと結論しました。 次に、材料名に関する調査を行いました。 クライアントから配信サーバーへ材料名を送信するといっても、1レシピに対してどのくらいの材料があるのか、材料名を連結するとどのくらいの長さになるのかということはネットワーク通信を考える際に重要な項目です。 材料数20個以下のレシピが全体の約99%、材料名の連結バイト数は 350Byte 未満のレシピが全体の約99%であることがわかりました。 そこで今回は、材料数20個以下、材料名を 350Byte までの材料を配信サーバーに送信することにしました。

f:id:marin72_com:20210730002500p:plain:w300 f:id:marin72_com:20210730002447p:plain:w300

キャッシュ戦略

ひとくちに『キャッシュ』と言っても、何をキャッシュするか、生存期間をどの程度に設定するか、などによってそのパフォーマンスは大きく変わります。材料ジャックの設計にあたって、考慮した点を紹介します。

配信サーバーはクックパッドのアクセスと同量のリクエストがある、広告の表示とインプレッション・クリック数の集計機能をもつ重要なサービスです。 大量のリクエストを捌くために Amazon ElastiCache (Memcached) を使用しており、ほとんどのリクエストはキャッシュで返しています。 材料ジャックの実装で雑にキャッシュを使ってしまった結果、キャッシュが溢れて広告全体のレスポンスに影響があるということも考えられます。 そこで、AWS 公式の ElastiCache のモニタリングすべきメトリクス を参考に事前調査を行いました。 CPU Utilization, Swap Usage, Evictions, CurrConnections のすべてを調べたところ ElastiCache には余裕がありました。 そこで今回は、材料名正規化テーブル (2万件) と、アクセスのあった一部のレシピのみキャッシュをしました。 必要最低限のものをキャッシュすることで、キャッシュも溢れることなく、配信サーバーは高速にレシピから concept_ids へ変換することができました。

キャッシュを使ったメリット・デメリット

材料名正規化テーブルをキャッシュストアに全部乗せて、配信 DB との通信の抑制を図ったことに関するメリット・デメリットは以下のようになっています。

メリット
  • パフォーマンス観点:配信 DB に問い合わせることがないので、DB への負荷が変わらない
  • 監視運用観点:新しいミドルウェアを追加していないので、監視対象が増えない
  • リスク観点:材料ジャックで障害が起きても (実装ミスにより concept_id が引けない、recipe_id のキャッシュができないなど) 既存の純広告やネットワーク広告は表示できる
  • 開発効率観点:従来のターゲット配信の仕組みに乗せることができた
デメリット
  • 材料名を変更しても、キャッシュが切れるまでは材料ジャックに反映されない
  • 材料名正規化テーブルのキャッシュが消えた場合、材料ジャックが表示されなくなる

上記2点のデメリットがあるのですが、材料ジャックは長期売の商品であることと、新しいレシピの追加、既存レシピで材料の変更に即追従する必要はないという品質 (=条件にマッチした場合 100% 掲出保証ではない) の商品なので、材料名正規化テーブルはキャッシュにのみ乗せることにし、配信 DB へのアクセスは最低限に抑えることにしました。 今回の開発を通じて、商品の特性に合わせて設計をすることが大事だと学びました。

今回は実施しなかった他の案について

本実装では材料一覧をリクエストヘッダーに含める方針を取りましたが、Pantry (クックパッドの API サーバー) にアクセスをしてレシピ ID から材料一覧を取る手法もありました。 この方針であればリクエストヘッダーサイズは小さくできますが、引き換えに Pantry へのリクエストが急増してしまいます。Pantry はクックパッドのサービス全体を支える API サーバーなので、キャパシティの調整やパフォーマンスへの影響の検証を入念に行う必要が出てきてしまいます。 検討の結果、影響範囲の最小化と工数の削減のため、Pantry を使用せず、広告配信サーバーに材料名をパラメータで直接渡す手法を採用しました。

おわりに

本稿では、材料ジャックの設計と開発について紹介しました。商品の特性を理解し、使用しているミドルウェアや扱うデータ (レシピ) に関する調査を行うことで、設計後大きな手戻りもなく実装を行うことができました。

クックパッドの広告開発チームでは、大規模トラフィックをさばく配信サーバーの開発や純広告・ネットワーク広告の運用、管理画面の開発から、広告の新商品開発まで幅広い開発を行っています。
広告に興味がある! toB 向けの新商品を開発したい! お金儲けに興味がある! 大規模なトラフィックをさばくサービスを開発したい! に当てはまる方も当てはまらない方も、ぜひ一緒に広告開発をしてみませんか?

クックパッドでは一緒に働く仲間を募集しておりますので、ご興味ありましたらこちらのサイトをご覧ください。

info.cookpad.com

*1:2017年時テストデータで行った Encoder-Decoder の正答率

*2:現在のキャッシュの Expired の設定は24時間

マイクロマネジメントは悪か?よりよい組織をつくるためのマネジメント形態についての考察

レシピ事業サービス基盤部で部長をやっています、新井(@SpicyCoffee66)です。引越しを機に MtG のカードをほとんど売ったはずなのに、そのときは存在しなかったポケモンカードのデッキが手元にあります。なぜ?

私は 2017 卒のエンジニアとしてクックパッドに入社し、様々な業務を経験した後に 2020 年の 8 月から部長となりました*1。最近はコードを書いていないので Techlife の執筆内容に迷ったのですが、今自分の中にある「優れた組織づくりについての考え方」をまとめてみることとしました。部長になる前にも、グループ長として小規模なチームマネジメントの経験があるとはいえ、それを含めても2年弱のマネージャー経験しか持っていないので、これが絶対の正解というわけではなく一つの考えとして読んでいただけると幸いです。

組織の存在理由

優れた組織づくりについて考えるために、まずは組織の存在理由について考えます。組織の目的については、書籍の数だけ種類が存在するほど多様な記述がありますが、この記事ではシンプルに「ある目標を達成すること」を組織の目的として考えます。多くの会社ではこの目標がミッションになるでしょう。クックパッドで言うと「毎日の料理を楽しみにする」ことが組織の目的となります*2

一般に、目標達成に重要な要素は、目標に向かう主体の "速度" と "方向" になります。したがって、目標に向かう速度と方向について、主体が「個人」であるよりも「組織」である方が優位であることが、組織をつくる理由の根幹となり、また、この優位性の大きい方が優れた組織ということになります。

f:id:spicycoffee:20210726151631p:plain
個人と組織の比較

速度についてはわかりやすいでしょう。人が集まる方がマンパワーが増えるため出力が増加します。また、これまでの組織に存在しない専門性を持ったメンバーが加入することで、組織全体のキャパシティが広がっていきます。

方向については、人数が多くなるほどブレる・間違いが多くなるといった印象を抱く方もいるかもしれません。「船頭多くして船山に上る」という言葉もあります。しかし、人間の認知能力には限界があるため、個人の視野では方向性を決定するための情報や、存在する課題を拾いきれないケースが多くあります。このことから、少なくとも成熟した組織においては、方向性の決定についても個人に対して優位性があると考えられます。

ここまでに、組織で目標に向かうことについて、個人でそれをおこなう際と比較した優位性を確認してきました。この考え方を延長して、優れた組織を

  1. 方向性の決定においてより精度が高く
  2. 決定した方向性に沿ってより速い速度で進むことができる

ものであると定義します。

優れた組織をつくるためのマネジメントについての考察

ここからは、前節の定義に沿って優れた組織をつくるためのマネジメントについて考察していきます。今回は、マネジメント形態の分類として一般に用いられる「マイクロ/マクロマネジメント」という概念をベースに考えを深め、最終的に優れた組織をつくるためのマネジメントについての考察をまとめます。

マネジメント形態の分類

「マイクロ/マクロマネジメント」という形態は、白黒二値にハッキリ分類できるものではなく、各組織でのやり方がグラデーション的に分布している前提のもと、それぞれの特徴を考えます。

マイクロマネジメント

マイクロマネジメントでは、マネージャーがメンバーに対して課題を細かくブレークダウンして伝達し、より詳細・具体の部分にまで指示を出します。そのため、マネージャーからメンバーへのコミュニケーションは作業内容の指示が中心となります。その特性上、施策の方向性がマネージャーの意図からブレることは少なくなりますし、トップダウンの施策決定が多いため、意思決定が速くなる傾向も見られます。

一方で、マネージャーの認知能力が組織の認知能力の限界値となるため、特に方向性決定の精度において、組織であることのメリットを活かしづらい形態であると言えます。課題の解釈や解決方法のアプローチに幅をもたせづらく、マネージャー自身がその設定をミスった際に、組織全体でリカバリーするような自浄作用は働きません。速度についても、詳細にまで細かく指示を出すような極端な例を考えると、マネージャーの想定以上の生産性が発揮されるケースは少なくなります。

また、より具体にまで目を届かせる必要がある以上、マネージャーの目線がより内向き・近視眼的になることも、方向性の決定について精度を落とす要因となります。組織の外や未来のことに目を向ける余裕を持てないと、限られた情報で部分最適に近い選択肢を取ってしまう可能性が高くなるからです。

f:id:spicycoffee:20210726152936p:plain
マイクロマネジメントにおけるマネージャーの意識

マクロマネジメント

対してマクロマネジメントでは、マネージャーはメンバーに現状の課題や目的を伝達するにとどめ、解決へのアプローチについてはメンバーに裁量を持たせます。したがって、マネージャーからメンバーへのコミュニケーションは、課題の共有やより俯瞰した視点での整合性の確認が中心となります。

課題のブレークダウンをなるべく抑えてそのままの粒度でメンバーに伝達することで、マネージャーにより近い粒度で課題を捉えるメンバーが増え、マネージャーの認知能力が組織のボトルネックになりづらくなります。具体的には、マネージャーの課題設定にミスがあった際にメンバーからのフィードバックで再設定が可能になるケースが増えたり、メンバーが独自の観点で課題を解釈することにより、マネージャーが思いつかないような解決策を発案することのできる可能性が高くなります。

より広い範囲の具体についてメンバーに移譲できているため、マネージャーは組織外のことや未来のことに目を向ける余裕ができ、全体最適となる選択肢を取るために必要な情報を集めることができるようになります。

一方で、課題や目的に向き合う視点が増える分、それをまとめ上げるはたらきが必要になります。この整備・工夫を怠ると、意思決定を形成するのに長く時間がかかったり、その方向性がブレる、あるいは全ての意見の間を取った中途半端なものになってしまうといった危険性があります。

f:id:spicycoffee:20210726153003p:plain
マクロマネジメントにおけるマネージャーの意識

マネジメント形態の比較

先ほどまでに述べてきた各マネジメント形態の特徴を以下の表(図)にまとめます。

f:id:spicycoffee:20210726152335p:plain
マネジメント形態の比較

対になる性質は様々ありますが、総じてマイクロマネジメントは、組織の行動における不確実性の低さと意思決定の速度に、マクロマネジメントは、"方向" についての意思決定の精度と実行部分の "速度" に優位性があると言えそうです。ここで一点注意すべきなのは、マイクロマネジメントにおける不確実性の低さはあくまで組織の行動に対してのもの(つまりは内情が把握しやすいということ)であり、目的に対しての不確実性を低減している(つまり正しい方向に進んでいる)ことを保証するものではないということです。同様に、意思決定の速度は組織が進む速度とイコールではなく、間違ったアプローチをとった場合、実行全体で見たときの速度は下がる可能性もあります。

これらのことを考えると、冒頭に定義した優れた組織を実現するためには、マクロマネジメントを採用する方が適していると考えられます。

マクロマネジメントがうまく機能するための条件

前節の比較により、優れた組織の実現のためには、可能であればマクロマネジメントを採用する方が適しているとの考えを得ました。しかし、マクロマネジメントがうまく機能するためには、その特徴から

  • メンバーが組織の目標や状況について正しく理解している
    • メンバーが理解できるように伝達する能力をマネージャーが有しているということでもあります
  • メンバーの実行能力がマネージャーのそれよりも優れている

という二つの条件を満たす必要があります。

前者については、主に組織の進む "方向" に関係する条件です。マクロマネジメントの特徴として、マネージャーの認知能力が組織のボトルネックになりづらくなることを挙げましたが、これは裏を返すと、メンバーがマネージャーの視野の外にある課題や解決策を探索・発見できるという前提に立っています。そのためには、メンバーがマネージャーと同じ解像度で組織の持っている目標や置かれている状況について理解しておく必要があります。一般的には "視座" と表現されることの多い素養ですが、これらの情報を主に握っているのはマネージャーである以上、マネージャーにはこれを自身の意図と合わせて伝達し続けることでメンバーに理解してもらう責任があります。一概に「視座の高さはメンバーの能力である」と捉えるのは間違いで、逆に「マネージャーが情報を下ろしてこないのが悪い」と主張し続けるのも建設的ではなく、双方の歩み寄りによって満たすべき条件であると考えます*3 *4

後者については、主に組織の進む "速度" に関する条件です。マネージャーはマネージャーとして、メンバーはメンバーとしてそれぞれ専門性を持って働いている以上、実行の詳細についてはメンバーに移譲した方が速度が出るのが一般的かとは思います。しかし、中には「プレイヤーから転向したばかりのマネージャー x ジュニアメンバー」といった組み合わせ等、詳細についてもマネージャーから細かく指示を出した方が速度が出るケースもあります。そういった場合、少なくとも短期的にはマイクロマネジメントの方が速度が出ることになるため、メンバーの実行能力が十分に高いということも、マクロマネジメントが優位性を発揮するための条件になります。

より優れた組織をつくるためのマネジメント形態の移行

前節の比較から、より優れた組織を作るためにはマクロマネジメントを採用する方がよいが、それが有効に働くためには組織の成熟度に関する条件が存在することがわかりました。「ジュニアメンバーを中心に構成されている組織において、抽象的な指示しか出さない」「成熟したメンバーが揃っている組織において、詳細についても指示を出し続ける」といった例を考えてみると、それぞれ組織活動が上手くいかないことが想像できるため、直感的にも正しい理解であると言えるでしょう。

この考えを押し進めることで、マイクロマネジメントとマクロマネジメントをマネジメントのステージとして捉え、なるべく マイクロマネジメントからマクロマネジメントへ移行していくことで、より優れた組織をつくる ことができるという考えに至ることができます。組織やメンバーの成熟度によってマネジメントの形態を使い分けながら、なるべくマクロなマネジメントに移行していくことで、組織の生産性を上げることができるという考え方です。現実的にマイクロマネジメントを採用する方が生産性が高くなる状況は存在しますが、それを「組織が成熟していない」状態だと捉える考え方であるとも言えるでしょう*5

f:id:spicycoffee:20210726152420p:plain
マネジメント形態のステージ

マネジメント形態の移行を実現するために必要なことについての考察

前節に提示したマネジメント形態の移行を実現するために、マネージャーとメンバーそれぞれに求められることを考え、記事を締め括ります。

マネージャーに求められること

マネジメント形態の選択と適した速度での移行

組織の舵取りについての責任がマネージャーにある以上、マネジメント形態の選択についても主にはマネージャーの意志のもとおこなわれることになります。組織とメンバーの成熟度を考えながら、どの粒度まで指示を出すべきなのか、どの粒度までの権限移譲が可能なのかを考え、メンバーとコミュニケーションを取る。その過程で少しずつ権限の移譲範囲を広げていき、よりマクロなマネジメント形態への移行を実現することが求められます。文字にするのは簡単ですが実行は非常に難しく、私も「移譲したつもりで詳細の把握を怠っていたら手戻りにつながった」など、これまで何度も失敗してきました。マネージャーに求められる能力は多様なものがありますが、この判断精度を上げることは、明確にマネージャーとしての成長につながると思っています。

メンバーの成長支援

"マネジメント形態の比較"節において、マクロマネジメントがより有効性を発揮するための条件として、

  • メンバーが組織の目標や状況について正しく理解している
  • メンバーの実行能力がマネージャーのそれよりも優れている

の二点をあげました。つまり、マネジメント形態の移行を実現するためには、マネージャーとしてこの二点にコミットする必要があるということになります。メンバーが成長するとうれしいといったような心理的な要因をあえて除き、組織論の観点からドライに考えたとしても、メンバーの成長を支援する理由がマネージャーには存在するのです。

前者については、組織の目標やその意義、置かれている状況について繰り返し伝達する必要があります。組織の定例、普段の雑談、メンバーの提案した施策 issue へのコメント等、折に触れて伝え続けることが重要です。特にメンバーの提案をリジェクトする場合、その原因となるマネージャーとメンバーの観点や持っている情報の差について丁寧に説明することで、お互いの思考を同期する機会とすることが可能です。

後者については、メンバーの成長になるような挑戦の機会を業務の中で設計したり、自己研鑽のための投資を推奨することが効果的になります。

どちらについても、実行におけるミスや足元の速度低下についてある程度のリスクを飲む必要はあるでしょう。メンバーの成長支援が中長期的な投資であることをしっかりと認識し、提供する挑戦機会やスケジュールのバッファなどを通して、リスクコントロールをすることもマネージャーの役割であると考えます。

メンバーに求められること

自身の成長

マネジメント形態の移行についてメンバーに求められることは、何よりも自身の成長になります。自身の成長が組織の成熟につながることをしっかりと認識し、能力を高めていくこと。もう一歩踏み込んだ話をすると、組織の成熟につながるような成長を目指すことが求められます*6。その成長についても、速度を上げるための実現能力の成長はもちろんのこと、方向性の精度を上げるための視野・視座についても、より広く、より高いものにしていくことが望ましいと考えられます。マネージャーから伝達された課題や状況といった情報を一旦咀嚼し、理解しきれなかったところは積極的に議論を持ちかけるなど、メンバーからの歩み寄りも重要になります。特に自分の提案がリジェクトされたタイミングにおいては「マネージャーの理解と自分の理解との間に大きな差がある」ことがほぼ確定するので、その差について積極的に議論し、吸収することが求められます。

最後に

この記事では、最初に組織の存在理由と優れた組織について定義し、それを実現するための方法を、組織の観点からマネジメント形態と、個人の観点からメンバー・マネージャーにそれぞれ求められることの両方について考えました。私自身は、組織を良くするのもあくまでより大きな価値をより早く提供するためというスタンスを持っていますが、同時に健全なプロダクトは健全な組織からしか生まれないという信念も持っています。

冒頭にも述べたように、この記事で述べたことが正解であるというつもりはないので、気になること等がありましたら是非 Twitter 等でコメントをいただけるとうれしいです。また、Meety に以下のカジュアル面談を掲載していますので「直接聞いてみたいことがある」「クックパッドに興味がある」といった方は気軽にご応募ください。 

meety.net

クックパッドでは、チーム開発で大きな価値を世に届けたい開発者を絶賛大募集中です。転職を考えている方は以下の採用サイトから、そこまでではないがクックパッドに興味が出てきた方は、上記のカジュアル面談から是非ご連絡いただけますと幸いです。 

info.cookpad.com

*1:過去の仕事から生まれた記事はこちら → spicycoffee66 の検索結果 - クックパッド開発者ブログ

*2:この辺りの組織の意義や構造に関する話は『すぐれた組織の意思決定』という書籍も参考になるかと思います

*3:"視座" については 専門職と視座. こんにちは。ミクシィでスポーツやライブエンタメ関連の技術部長を担当している石井で… | by Kunzo Ishii | mixi developers | Medium も参考になります

*4:視座は能力だけの問題じゃないよというような話は オープンでフラットな組織が突然「閉鎖的」と言われるとき|柴田史郎|note にも

*5:組織形態を「ステージ」として捉えるという考え方は『ティール組織』に出てくる考え方に近い発想かもしれません

*6:ここでは「個人の市場価値を高める」という目的については述べていません

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/