データ系エンジニアの職種の違い

はじめに

自分は Martin Kleppmann が言うデータ指向アプリケーションやそれを実現する周辺の技術領域が好きで、業務としてそのような領域のエンジニアリングを引き続きやっていけたらなと思っています。 世の中には関連する職種の求人が多々ありますが、同じ名前のロールでも職務内容がコンテキストによって異なることが多かったりします。 ここではそれぞれの職種の違いについて自分の観点からまとめます。

1. データエンジニア

求人を眺めていると、データエンジニアは企業によって割と役割がぶれるので分けて説明します。

1-1. 小さめの事業会社のデータエンジニア

まずは、小さめの事業会社のデータ分析基盤の構築・運用をするロールです。 ここでは ETL 処理の実装・運用のほかに、各種ツールを使ったデータ基盤の構築・運用知識やクラウド上のアプリケーション構築の知識などが求められることが多いです。 さらには、データ分析業務や機械学習アプリケーションの構築もそこに含まれる場合もあります。

1-2. 大きめの事業会社のデータエンジニア

一方、例えば保険系や金融系なんかの大きめのトラディショナルな企業ではより職責が狭くなって、複雑な ETL 処理をコード化したり、そのトラブルシュートをしたりするような職種であることが多いです(以前 Apple もデータエンジニアの求人を出していましたが、話を聞いたところそんなそんな感じでした)。 このような企業では小さめの事業会社で求められる能力とは異なり、インフラやツールセットの知識よりも ETL の実装そのものに比重が置かれます。

1-3. コンサル企業のデータエンジニア

さらに、コンサルサービスを提供する企業でもデータエンジニアの求人を募集していることがあります。 上記二つのケースがインハウスの社内システムに関するロールである一方、こちらはお客様のデータ処理基盤の面倒をみるロールとなります。 上記のプロダクト向かいのロールと違い、お客様向かいになるので、求められる能力は技術知識以外にもお客様と関係性を維持しながらプロジェクトを運用していく能力が求められます。

2. ソフトウェアエンジニア(データプラットフォーム、MLOps)

こちらは、自社のデータ処理関連プラットフォームをソフトウェアエンジニアとして開発していくロールです。 社内外向けのサービスを開発するロールとなり、バリバリにコードを書く能力が求められます。 (現職のスマートニュースも自分が入社したころはデータエンジニアとして求人をしていましたが、現在はこちらの肩書きで募集しています) MLOps エンジニアとなる場合は、特徴量エンジニアリングなどではデータ処理基盤と重なる部分はありますが、効率的なトレーニングやサービングなどの機械学習ドメインに関する知見が特に重視されるでしょう。

4. ソリューションアーキテクト(データ処理関連プロダクト)、ビックデータコンサルタント

テックベンダーかコンサル企業で、ソリューションかコンサルサービスを提供し顧客サポートをするロールとなります。 コンサル企業のデータエンジニアと同様に、顧客向かいのロールで、技術的知見以外の能力が求められます。

5. データアーキテクト

そんなにポジションが存在する職種ではなく、データアーキテクトは企業によって役割は異なることが多いです。 基本的にはインハウスのシステムのデータ設計やプロジェクトリードなんかが主な業務だと思います(ドメイン知識が必要なことが多い)。 一方で、ソリューションアーキテクト的なロールであることもあります。

6. SRE

インフラ知識のあるエンジニアは、SRE としても働いて良いかもしれません。データエンジニアのロールを用意せず、SRE やインフラエンジニアがデータ基盤をインフラの一部として面倒を見るようなステージの企業は多いと思います。 この場合、データ基盤技術領域以外の SRE 的なインフラ的な知見が求められます。

まとめ

上記にあげたものの他にも、Analytics Engineer や Database Reliability Engineer、Data Reliability Engineer だったり、企業ごとに独自の役割を付与してロールにしていることがあります。 これは、この問題領域におけるロールが比較的新しく、それぞれの組織の運用の形に合わせて手探りで役割を与えていることによるものでしょう。

キャリア面に関しては、データエンジニアを続けていくと、将来その先はどうなるだろうかという話はあります。 IC でいることに飽きてマネージメントに挑戦したくなった場合、データエンジニアはわりと条件が厳しい印象があります。 BigQuery 含むマネージドサービスの勃興で企業規模に対して必要なインハウスのエンジニア数が少なくて済むようになっており、小さな企業ではマネージャーを置くまでチーム規模を大きくする必要がないからです。 一方、事業規模が大きくなりデータエンジニアが多めに必要な事業体を選ぶとなると、小さな事業体とは必要とされる能力も異なってきて、ゲームが変わってきます。

DX 文脈もあり、関連技術の重要性は増すでしょう。キャリアの方向性を考えるためにも、市場でどんなロール・能力が求められているか、自分はどの方向性に向かいたいかを継続的に確認していくことは重要かと思います。

2019年買って良かったものと今欲しいもの

2019年買って良かった物

基本的に必要にかられないと買い物はしないが(書籍は除く)、今年に買って良かった物を紹介する。

第5位 BRAUNのハンドブレンダー

みじん切りとか楽にしたりスムージーとかしたいねとずっと話していたが、踏み切れず先延ばしにしていたところ、妻が義弟から誕生日のプレゼントとしていただいた(買ったものじゃなかった)。 牛乳とバナナなどで手軽に健康的な気持ちになれるのが良い。

第4位 GREGORYのCOVERT MISSION DAY

5年使っていた普段使い用のリュックのチャックが壊れてしまったので、良さそうな物をインターネットで調べて、ちょうど会社の前にあった GREGORY で購入した。 22L あってそこそこ何でも入るし、ポケットやスリーブも使いやすく、背負った時のフィット感も良い。 しいて言えば、背面がメッシュじゃないのでちょっと蒸れるかも。 www.gregory.jp

第3位 THE BODY SHOPのインディアンナイトジャスミン ボディクリーム

妻の買い物ついでに物色していたところ、香りが良かったので購入した。その際に店員さんには「男性が好まれるんですよねー」ってコメントされたが、どんな相関があるのか謎。香りが好きで気に入って使っている。

www.the-body-shop.co.jp

第2位 机と椅子

引っ越す前は狭めの1DKに二人暮らししていて、フリーランスの妻には仕事部屋がなかった。 簡素な折りたたみ机(毎日しまう)と椅子で仕事をしていて、非常に辛そうだった。 少し広めの部屋に引っ越し、やっと机と椅子が置けて仕事が捗っているようで本当に良かった。 椅子はニトリのこれ

www.nitori-net.jp

あと、引っ越し時、ダイニング用のテーブルと椅子も購入する必要があり、椅子は質感が気に入って一目惚れした下記のものにさせてもらった(本当はそんなお金かける気もなかったが、直感に従ってしまった)。

https://www.idc-otsuka.jp/item/item2.php?id=dchair377www.idc-otsuka.jp

第1位 マンション

部屋のグレードが上がり、持ち家か賃貸か論争も気にならなくなり、不動産や税金の知識がつき、35年のローンを背負った。 まだ引っ越して1ヶ月だけれど、あれだけ悩んで買っただけあって非常に満足している。 あと、風呂場にミストサウナがついており、購入時はあまり期待していなかったが、使ってみて非常に気に入った。水風呂を張れば簡易交代浴できそう。

2019年の今現在欲しい物

第3位 AirPods Pro

欲しい。けど、なくても問題ない。

第2位 Switchとリングフィットアドベンチャーとフィットボクシング

運動をゲーミフィケーションされたい。どれくらい続くかな。

第1位 海外リゾートホテルコンドミニアム

欲しい。

ja.sekaiproperty.com

聴いているPodcastの番組

通勤時間が徒歩+電車で片道30分超くらいで、その道すがらPodcastを聴いています。コンテンツはTechニュースが主で、英語のリスニングも兼ねていて、稀にshadowingもしたりします。以下に、聴いているPodcastを紹介します。その他におすすめの番組あれば教えてください。

日本語編

Rebuild

定番ですが、好きです。個人的にはNさん回が好きで、その他ゲーム回は割とスキップしたりします。家で流していたら妻がよく登場するゲストを識別できるようになりました。

www.listennotes.com

バイリンガルニュース

これも定番。話題になっている社会問題からScienceのニッチなニュースまで幅広く知れます。取り上げるテーマがユニークなので好きです。

www.listennotes.com

Misreading Chat

CSの論文をざっくばらんに紹介してくれていて、とても勉強になります。

www.listennotes.com

英語編

TechCrunch

TechCrunch の Podcast です。短くてわかりやすいのが良いです。

www.listennotes.com

Software Engineering Daily

特定の技術領域に特化した話が聞けて勉強になります。ホストの Jeff 氏の英語も聞き取りやすくて良いです。

www.listennotes.com

Modern CTO with Joel Beasley

有名企業の CTO / VPoE をゲストに生い立ちから技術の話まで聞けて面白いです。英語も聞き取りやすいです。

www.listennotes.com

Data Engineering Podcast

Data Engineering に特化した Podcast。テーマは面白いですが、ホストの方の英語が抑揚なくやや聞き取りにくいです。

www.listennotes.com

This Week in Startups

日本でも翻訳されているエンジェル投資家の著者であるジェイソン・カラカニス氏の配信しているPodcast 。投資家視点の話が聞けて面白いです。

www.listennotes.com

a16z Podcast

Andreessen Horowitz が配信している Podcast。たまに聞きます。

www.listennotes.com

16 Minutes News by a16z

Andreessen Horowitz が配信している Podcast だけれどニュースに特化したもの。数ヶ月に始まったものですが、こっちのが a16z Podcast より短いし聞きやすいです。

www.listennotes.com

Equity

TechCrunch の VC が配信している Podcast。たまに聞きます。

www.listennotes.com

(翻訳) データエンジニアリングの未来

訳者まえがき

原著者の Chris Riccomini の許可を得て以下の記事を翻訳・公開しました。

riccomini.name

下記より記事翻訳本文です。

データエンジニアリングの未来

https://riccomini.name/assets/images/2019-07-29-future-data-engineering/mathilda-khoo-HLA3TAFQuQs-unsplash.jpg

私は最近、近頃のデータエンジニアリングがこれまで来た道について、また、この分野の仕事の将来について考えてきました。考えのほとんどは、私たちのチームが WePay で実践していることを背景にしています。その一方、以下に述べる考えは普遍的で、共有する価値があるものと思っています。

データエンジニアリングの仕事は、組織におけるデータの移動と処理を支援することです。これには、一般的に、データパイプラインとデータウェアハウスという2つの異なるシステムが必要です。データパイプラインはデータの移動を担当し、データウェアハウスはデータの処理を担当します。これは、やや過度に単純化しています。バッチ処理とストリーム処理では、抽出(extraction)とロード(loading)の間で変換(transformation)を行うことにより、パイプライン自体で処理を行うことはできます。近年の データウェアハウスには、多くのストレージおよび処理システム(Flink、Spark、Presto、Hive、BigQuery、Redshiftなど)と、データカタログ、ジョブスケジューラなどの補助システムが含まれています。しかし、それでもこのパラダイムは意味のあるものと思います。

業界では、これらのシステムの構築および管理方法の変革が進んでいます。特に、今後数年間で変化が予期される、4つの分野があります。

  • 適時性(Timeliness): バッチからリアルタイムへ
  • 接続性(Connectivity): one:one、特注のインテグレーションから many:many へ
  • 集中化(Centralization): 集中管理からセルフサービスツールへ
  • 自動化(Automation): 手動管理から自動化ツールへ

バッチからリアルタイムへ

データパイプラインとデータ処理の両方は、これまではバッチベースでした。データはバッチETLスナップショットでシステム間転送が行われ、ジョブスケジューラ(Airflow、Oozie、Azkaban、Luigi)によって定期的に処理されていました。

最近は、リアルタイムデータパイプラインとリアルタイムデータ処理システムへ移行しつつあります。 Debezium などの変更データキャプチャシステムと、Kafkaの堅牢なコネクタエコシステムにより、リアルタイムのデータパイプラインが可能になりました。ストリーム処理は、過去5年間でルネッサンスを迎え、リアルタイムのデータ処理システムは現在追いきれないほど多くなっています。これら要因により、つまり、入力(抽出)、処理(変換)、および出力(読み込み)がすべてリアルタイムで行われることを意味します。

現在のトレードオフは、コストと複雑さのようです。企業がデータウェアハウスを立ち上げており、4〜6週間ですぐに効果が必要な場合、リアルタイムパイプラインをセットアップするのは大きな負担になります。バッチの作成と実行は依然として簡単です。リアルタイム領域のツールが成熟し続け、クラウドホスティングが成長するにつれて、これは今後数年間で変わると私は予想しています。

接続性

上流のデータソースをデータウェアハウスに接続することは、システム間の1to1接続ごとにまったく新しい特注のインテグレーションを追加することを意味していました。Jay Krepsの重要な投稿、The Log: What every software engineer should know about real-time data’s unifying abstraction は、これをよく説明しています。

https://riccomini.name/assets/images/2019-07-29-future-data-engineering/datapipeline_complex.png

複雑なデータパイプライン

この投稿は、データ統合の将来についても詳しく説明しています。これは2013年に書かれたもので、そこで予想され提案されているものの多くが現在に実を結び始めています。 ConfluentKafka Connect、およびコネクタエコシステムが作られたことで、既存の Kafka データパイプラインに接続できる実行可能なコネクタが数多く生まれました。

このアーキテクチャのアプローチは定着していくでしょう。入力を出力から、またそれらを変換から切り離することは、データパイプラインにメトカーフの法則を効き始めることを意味します。そこでは、新しいシステムをパイプラインに追加することは、以前よりも安価でより価値があります。

上記のパラグラフはかなり Kafka 中心主義的であるとは思いますが、このアプローチにおいてはリアルタイムは必ずしも必要ありません(そうなっていくとは思いますが)。たとえば Airflow では、現在 GoogleCloudStorageHook、BigQueryHook、1to1のオペレーターであるGoogleCloudStorageToBigQueryOperator があります。出力から入力を完全に解きほぐし、一般的な形式のステージング領域が存在すると、バッチベースのETLの改善にも大いに役立ちます。

このパターンにより、よりきめ細かいデータシステムを採用することもできます。パイプラインに新しいシステムを接続するのが安価になると、エコシステムに新しい特殊なシステムを追加する価値がそのコストを上回ります。その結果、グラフデータベース、リアルタイムOLAPシステム、検索インデックスなど、ニッチなデータ処理システムの使用が増えることが期待されます。また、新しいシステムをパイプラインに接続し、それがニーズに合わないことを確認することが以前よりもはるかに安価になるため、このパターンはより多くの実験を可能にします(これは今年の初めの記事ですでに取り上げたテーマです, Kafka is your escape hatch)。

クラウドもまた、接続性の一連の作業に興味深い妙案を加えてくれています。データ統合の作業を、AWSコンソールのチェックボックスにするという夢はまだ実現していません。各クラウドプロバイダーで、すべてのシステムはすぐに完全に統合されることはないでしょう。ポイントアンドクリックUIでのクラウドプロバイダー間を完全統合するというアイディアは、さらに遠そうです。最後に、クラウドおよび非クラウド(またはサードパーティがホストする)システムを統合するには、依然として作業が必要です。

これらの理由から、Stitchなどのクラウドベースのサードパーティソリューションは引き続き価値があると思います。また、これは、上記で説明したリアルタイムのKafkaアーキテクチャが、構築して運用する余裕があるとすれば、最も成熟したソリューションになることを意味します。

自動化と非中央集権化

リストの最後の2つの項目、自動化と集中化は、かなり密接に関連しています。ほとんどの組織には、データパイプラインとデータウェアハウスを管理する単一のデータエンジニアリングチームやデータウェアハウジングチームがいます。これらのチームにリクエストが届くと、次の2つの基準でリクエストを評価する必要があります。

  • 技術的に可能か: What can we do (technical)
  • ポリシー的に可能か: What may we do (policy)

私の経験では、中央集権化されたチームは通常、ある程度の自動化を行いますが、技術的な自動化に主に焦点を合わせます。これは、エンジニアリングチームの操舵室にあるため、当然です。この種の作業は、通常、運用上のトイルを自動化することを意味します。新しいコネクタの追加、監視またはデータ品質チェックの設定、新しいデータベースまたはテーブルの作成、許可の付与などの作業です。

ただし、データエンジニアリングがまだ自動化していないもうひとつのタイプのトイルがあります。ポリシーのトイルです。この種の面倒な作業には、誰がどのデータにアクセスできるか、どのくらいの期間データを保持するか、どのデータシステムにどのような機密データを格納することを許すか、どの地域のデータが存在するかについての決定が含まれます。通常、データエンジニアリングは、これらの質問に対する回答を最終的に決定するチームではありませんが、回答を見つける際に連絡役またはドライバーとして行動しなければならないことがよくあります。これは通常、セキュリティ、コンプライアンス、法律など、組織の他のチームを介して要望をナビゲートすることを意味します。

GDPRCCPAなどの規制により、すでにこの種の作業は重要です。従来のソフトウェア企業の枠を超え、ヘルスや金融などの分野において技術の継続的な拡大に対する政府の規制を追加する場合(Why Software Is Eating the World 参照)、ポリシーのトイルを自動化することの重要性が増すことは避けられないでしょう。

ポリシーの自動化には、成熟度の低いデータエコシステムでは通常無視されている分野に焦点を当てる必要があります。 LyftAmundsenApache RangerGoogleデータカタログなどのツールを採用する必要があるでしょう。監査、DLP、機密データの検出、保持期間の強制、アクセス制御管理などのポリシーの実施は、すべて完全に自動化する必要があります。

自動化が技術分野とポリシー分野の両方で成熟するにつれて、必然的に次のような質問が生まれます。なぜ、単一のチームで管理する必要があるのでしょう? ツールがポリシーガイドラインを実行し、データパイプラインを自動化するならば、組織のそれぞれのチームがデータパイプラインとデータウェアハウスを直接管理できるようにするのはどうでしょうか?

データパイプラインにおいては非中央集権化とは、技術的およびポリシーガイドラインに(自動的に実施される)準拠している場合、チームが既存のデータパイプラインに接続することを決定できることを意味します。データウェアハウスにおいては、チームは必要に応じてデータベース、データセット、データマート、およびデータレイクを作成できます(一部は既に存在しています)。これにより、多くの複雑性、混乱、および重複が発生します。そのため、ツール(上記に挙げたツールなど)が非中央集権化にとって非常に重要な前提条件となります。

非中央集権化のトピックは、Zhamak Dehghaniの投稿 How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh で詳細に説明されています。ぜひお読みください。より複雑なデータエコシステムを管理するために認知的負荷が生まれてしまうため、唯一のスケーラブルで効率的な方法は、自動化と非中央集権化だと思います。これは、ある意味では、アプリケーション層で過去10年間に見られたCI / CDおよびモノリスからマイクロサービスへの移行のように見えます。

結び

これらを踏まえて、私は楽観的です。まだやるべきことがあり、たくさんの機会があります。これらの要求を満たすために、組織内でデータエンジニアリングが拡大することを期待しています。オープンソースコミュニティは拡大を続け、新しいプロジェクトとスタートアップが生まれます。これらのすべてが、組織全体の大幅な効率向上につながり、より厳密なデータ管理の実践につながるはずです。

JMX metrics exposed in Presto version 302

Summary

Presto provides JMX metrics to monitor its system but there is few description in the document what metrics we can use, so I checked it.

Monitoring · prestosql/presto Wiki · GitHub

I used CLI tool jmxterm to get the information.

Commands

$ echo domains | java -jar jmxterm-1.0.0-uber.jar -l localhost:8086 -n > domains.txt
$ sed -i "s/^/beans -d /g" domains.txt
$ cat domains.txt | java -jar jmxterm-1.0.0-uber.jar -l localhost:8086 -n > beans.txt
$ cat beans.txt | awk -F: '{print "info -d " $1 " -b " $2}' | java -jar jmxterm-1.0.0-uber.jar -l localhost:8086 -n > info.txt 2>&1

Results

domains

[ec2-user@ip-10-0-101-241 ~]$ cat domains.txt
JMImplementation
com.amazonaws.management
com.sun.management
io.airlift.discovery.client
io.airlift.discovery.store
io.airlift.event.client
io.airlift.http.client
io.airlift.http.server
io.airlift.jmx
io.airlift.log
io.airlift.node
io.airlift.stats
java.lang
java.nio
java.util.logging
org.eclipse.jetty.http2.server
org.eclipse.jetty.io
org.eclipse.jetty.jmx
org.eclipse.jetty.server
org.eclipse.jetty.server.handler
org.eclipse.jetty.server.handler.gzip
org.eclipse.jetty.servlet
org.eclipse.jetty.util.thread
org.eclipse.jetty.util.thread.strategy
presto.execution
presto.execution.executor
presto.execution.resourceGroups
presto.execution.scheduler
presto.failureDetector
presto.memory
presto.metadata
presto.operator.index
presto.plugin.hive
presto.plugin.hive.metastore
presto.plugin.hive.metastore.thrift
presto.plugin.hive.s3
presto.security
presto.server
presto.server.remotetask
presto.spiller
presto.sql.gen
presto.sql.planner.iterative
presto.sql.planner.optimizations
sun.nio.ch

beans

[ec2-user@ip-10-0-101-241 ~]$ cat beans.txt
JMImplementation:type=MBeanServerDelegate
com.amazonaws.management:type=AwsSdkMetrics
com.amazonaws.management:type=AwsSdkMetrics/1
com.sun.management:type=DiagnosticCommand
com.sun.management:type=HotSpotDiagnostic
io.airlift.discovery.client:name=Announcer
io.airlift.discovery.client:name=ServiceInventory
io.airlift.discovery.store:name=dynamic,type=DistributedStore
io.airlift.discovery.store:name=dynamic,type=HttpRemoteStore
io.airlift.discovery.store:name=dynamic,type=Replicator
io.airlift.event.client:name=EventClient
io.airlift.http.client:name=ForDiscoveryClient,type=HttpClient
io.airlift.http.client:name=ForDynamicStore,type=HttpClient
io.airlift.http.client:name=ForEventClient,type=HttpClient
io.airlift.http.client:name=ForExchange,type=HttpClient
io.airlift.http.client:name=ForFailureDetector,type=HttpClient
io.airlift.http.client:name=ForMemoryManager,type=HttpClient
io.airlift.http.client:name=ForNodeManager,type=HttpClient
io.airlift.http.client:name=ForScheduler,type=HttpClient
io.airlift.http.client:name=ForWorkerInfo,type=HttpClient
io.airlift.http.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=httpserverchannellistener
io.airlift.http.server:id=0,type=classpathresourcehandler
io.airlift.http.server:id=0,type=statsrecordinghandler
io.airlift.http.server:id=1,type=classpathresourcehandler
io.airlift.http.server:name=HttpServer
io.airlift.http.server:name=RequestStats
io.airlift.jmx:name=StackTraceMBean
io.airlift.log:name=Logging
io.airlift.node:name=NodeInfo
io.airlift.stats:name=PauseMeter
java.lang:name=Code Cache,type=MemoryPool
java.lang:name=CodeCacheManager,type=MemoryManager
java.lang:name=Compressed Class Space,type=MemoryPool
java.lang:name=G1 Eden Space,type=MemoryPool
java.lang:name=G1 Old Gen,type=MemoryPool
java.lang:name=G1 Old Generation,type=GarbageCollector
java.lang:name=G1 Survivor Space,type=MemoryPool
java.lang:name=G1 Young Generation,type=GarbageCollector
java.lang:name=Metaspace Manager,type=MemoryManager
java.lang:name=Metaspace,type=MemoryPool
java.lang:type=ClassLoading
java.lang:type=Compilation
java.lang:type=Memory
java.lang:type=OperatingSystem
java.lang:type=Runtime
java.lang:type=Threading
java.nio:name=direct,type=BufferPool
java.nio:name=mapped,type=BufferPool
java.util.logging:type=Logging
org.eclipse.jetty.http2.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=abstracthttp2serverconnectionfactory$http2sessioncontainer
org.eclipse.jetty.http2.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=http2cserverconnectionfactory
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=arraybytebufferpool
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=connectionstatistics
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=managedselector$selectorproducer
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=1,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=1,type=managedselector$selectorproducer
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=2,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=2,type=managedselector$selectorproducer
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=3,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=3,type=managedselector$selectorproducer
org.eclipse.jetty.jmx:id=0,type=mbeancontainer
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=abstractconnector$acceptor
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=httpconfiguration
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=httpconnectionfactory
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=serverconnector
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=serverconnector$serverconnectormanager
org.eclipse.jetty.server:id=0,type=server
org.eclipse.jetty.server.handler:id=0,type=errorhandler
org.eclipse.jetty.server.handler:id=0,type=handlercollection
org.eclipse.jetty.server.handler:id=0,type=handlerlist
org.eclipse.jetty.server.handler:id=0,type=requestloghandler
org.eclipse.jetty.server.handler:id=0,type=statisticshandler
org.eclipse.jetty.server.handler.gzip:context="ROOT@@http",id=0,type=gziphandler
org.eclipse.jetty.server.handler.gzip:id=0,type=gziphandler
org.eclipse.jetty.server.handler.gzip:id=1,type=gziphandler
org.eclipse.jetty.servlet:context="ROOT@@http",id=0,type=servletcontexthandler
org.eclipse.jetty.servlet:context="ROOT@@http",id=0,type=servlethandler
org.eclipse.jetty.util.thread:context=HTTP/1.1|h2c@183fc2fa,id=0,type=scheduledexecutorscheduler
org.eclipse.jetty.util.thread:id=0,type=queuedthreadpool
org.eclipse.jetty.util.thread:id=0,type=reservedthreadexecutor
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=0,type=eatwhatyoukill
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=1,type=eatwhatyoukill
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=2,type=eatwhatyoukill
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=3,type=eatwhatyoukill
presto.execution:name=QueryExecution
presto.execution:name=QueryManager
presto.execution:name=RemoteTaskFactory
presto.execution:name=TaskManager
presto.execution.executor:name=MultilevelSplitQueue
presto.execution.executor:name=TaskExecutor
presto.execution.resourceGroups:name=InternalResourceGroupManager
presto.execution.resourceGroups:name=chartio,type=InternalResourceGroup
presto.execution.resourceGroups:name=user,type=InternalResourceGroup
presto.execution.scheduler:name=NodeScheduler
presto.execution.scheduler:name=SplitSchedulerStats
presto.execution.scheduler:segment=all
presto.execution.scheduler:segment=machine
presto.failureDetector:name=HeartbeatFailureDetector
presto.memory:name=ClusterMemoryManager
presto.memory:name=general,type=ClusterMemoryPool
presto.memory:name=general,type=MemoryPool
presto.memory:name=reserved,type=ClusterMemoryPool
presto.memory:name=reserved,type=MemoryPool
presto.metadata:name=DiscoveryNodeManager
presto.operator.index:name=IndexJoinLookupStats
presto.plugin.hive:name=hive,type=FileFormatDataSourceStats
presto.plugin.hive:name=hive,type=HiveSplitManager
presto.plugin.hive:name=hive,type=HiveWriterStats
presto.plugin.hive:name=hive,type=NamenodeStats
presto.plugin.hive:name=hive,type=OrcFileWriterFactory
presto.plugin.hive:name=hive_ad,type=FileFormatDataSourceStats
presto.plugin.hive:name=hive_ad,type=HiveSplitManager
presto.plugin.hive:name=hive_ad,type=HiveWriterStats
presto.plugin.hive:name=hive_ad,type=NamenodeStats
presto.plugin.hive:name=hive_ad,type=OrcFileWriterFactory
presto.plugin.hive.metastore:name=hive,type=CachingHiveMetastore
presto.plugin.hive.metastore:name=hive_ad,type=CachingHiveMetastore
presto.plugin.hive.metastore.thrift:name=hive,type=ThriftHiveMetastore
presto.plugin.hive.metastore.thrift:name=hive_ad,type=ThriftHiveMetastore
presto.plugin.hive.s3:name=hive,type=PrestoS3FileSystem
presto.plugin.hive.s3:name=hive_ad,type=PrestoS3FileSystem
presto.security:name=AccessControlManager
presto.server:name=AsyncHttpExecutionMBean
presto.server:name=ExchangeExecutionMBean
presto.server:name=StatementHttpExecutionMBean
presto.server:name=TaskExecutorResource
presto.server:name=TaskResource
presto.server.remotetask:name=RemoteTaskStats
presto.spiller:name=SpillerFactory
presto.sql.gen:name=ExpressionCompiler
presto.sql.gen:name=JoinCompiler
presto.sql.gen:name=JoinFilterFunctionCompiler
presto.sql.gen:name=OrderingCompiler
presto.sql.gen:name=PageFunctionCompiler
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AddExchangesBelowExchangePartialAggregationGroupId
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AddExchangesBelowProjectionPartialAggregationGroupId
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AddIntermediateAggregations
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AggregationExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ApplyExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=CheckNoPlanNodeMatchesRule
presto.sql.planner.iterative:name=IterativeOptimizer,rule=CreatePartialTopN
presto.sql.planner.iterative:name=IterativeOptimizer,rule=DetermineJoinDistributionType
presto.sql.planner.iterative:name=IterativeOptimizer,rule=EliminateCrossJoins
presto.sql.planner.iterative:name=IterativeOptimizer,rule=EvaluateZeroLimit
presto.sql.planner.iterative:name=IterativeOptimizer,rule=EvaluateZeroSample
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ExtractSpatialInnerJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ExtractSpatialLeftJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=FilterExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ImplementBernoulliSampleAsFilter
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ImplementFilteredAggregations
presto.sql.planner.iterative:name=IterativeOptimizer,rule=InlineProjections
presto.sql.planner.iterative:name=IterativeOptimizer,rule=JoinExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeAdjacentWindowsOverProjects
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeFilters
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimitWithDistinct
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimitWithSort
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimitWithTopN
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimits
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MultipleDistinctAggregationToMarkDistinct
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PickTableLayoutForPredicate
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PickTableLayoutWithoutPredicate
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ProjectExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneAggregationColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneAggregationSourceColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneCountAggregationOverScalar
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneCrossJoinColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneFilterColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneIndexSourceColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneJoinChildrenColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneJoinColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneLimitColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneMarkDistinctColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneOrderByInAggregation
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneOutputColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneProjectColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneSemiJoinColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneSemiJoinFilteringSourceColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneTableScanColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneTopNColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneValuesColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneWindowColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushAggregationThroughOuterJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushLimitThroughMarkDistinct
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushLimitThroughProject
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushLimitThroughSemiJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushPartialAggregationThroughExchange
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushPartialAggregationThroughJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushProjectionThroughExchange
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushProjectionThroughUnion
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushRemoteExchangeThroughAssignUniqueId
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushTableWriteThroughUnion
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushTopNThroughUnion
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveEmptyDelete
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveFullSample
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveRedundantIdentityProjections
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveTrivialFilters
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveUnreferencedScalarApplyNodes
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveUnreferencedScalarLateralNodes
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ReorderJoins
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RewriteSpatialPartitioningAggregation
presto.sql.planner.iterative:name=IterativeOptimizer,rule=SimplifyCountOverConstant
presto.sql.planner.iterative:name=IterativeOptimizer,rule=SingleDistinctAggregationToGroupBy
presto.sql.planner.iterative:name=IterativeOptimizer,rule=SwapAdjacentWindowsBySpecifications
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedInPredicateToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedLateralJoinToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedScalarAggregationToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedScalarSubquery
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedSingleRowSubqueryToProject
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformExistsApplyToLateralNode
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformUncorrelatedInPredicateSubqueryToSemiJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformUncorrelatedLateralToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ValuesExpressionRewrite
presto.sql.planner.optimizations:name=PlanOptimizer,optimizer=AddExchanges
presto.sql.planner.optimizations:name=PlanOptimizer,optimizer=PredicatePushDown
sun.nio.ch:context=HTTP/1.1|h2c@183fc2fa,id=0,type=serversocketchannelimpl

bean info

[ec2-user@ip-10-0-101-241 ~]$ cat info.txt
Welcome to JMX terminal. Type "help" for available commands.
#mbean = JMImplementation:type=MBeanServerDelegate
#class name = javax.management.MBeanServerDelegate
# attributes
  %0   - ImplementationName (java.lang.String, r)
  %1   - ImplementationVendor (java.lang.String, r)
  %2   - ImplementationVersion (java.lang.String, r)
  %3   - MBeanServerId (java.lang.String, r)
  %4   - SpecificationName (java.lang.String, r)
  %5   - SpecificationVendor (java.lang.String, r)
  %6   - SpecificationVersion (java.lang.String, r)
#there's no operations
# notifications
  %0   - javax.management.MBeanServerNotification(JMX.mbean.unregistered,JMX.mbean.registered)
#mbean = com.amazonaws.management:type=AwsSdkMetrics
#class name = com.amazonaws.metrics.MetricAdmin
# attributes
  %0   - CredentialFile (java.lang.String, rw)
  %1   - HostMetricName (java.lang.String, rw)
  %2   - JvmMetricName (java.lang.String, rw)
  %3   - MachineMetricsExcluded (boolean, rw)
  %4   - MetricNameSpace (java.lang.String, rw)
  %5   - MetricQueueSize (java.lang.Integer, rw)
  %6   - MetricsEnabled (boolean, r)
  %7   - PerHostMetricsIncluded (boolean, rw)
  %8   - QueuePollTimeoutMilli (java.lang.Integer, rw)
  %9   - Region (java.lang.String, rw)
  %10  - RequestMetricCollector (java.lang.String, r)
  %11  - ServiceMetricCollector (java.lang.String, r)
  %12  - SingleMetricNamespace (boolean, rw)
# operations
  %0   - void disableMetrics()
  %1   - boolean enableDefaultMetrics()
(以下省略)

ベン・ホロウィッツの「HARD THINGS」を読んだ

最近、組織的な動きによって問題の解決が可能かを考える場面がたまにあるので、何かヒントになることでもないかと思い、前から読んでみたかった本書を読んだ。

HARD THINGS

HARD THINGS

良かったところを以下に述べる。

CEOの苦闘の話

前半の苦難が詰まったエピソードを読んだ後に、CEOの苦難が語られ始める。 特に下記のフレーズ。他の文書と違ってやたら詩的でぐっとくる。

苦闘とは、そもそもなぜ会社を始めたのだろうと思うこと。苦闘とは、あなたはなぜ辞めないのかと聞かれ、その答えを自分もわからないこと。苦闘とは、社員があなたはウソをついていると思い、あなたも彼らがたぶん正しいと思うこと。

苦闘とは、料理の味がわからなくなること。苦闘とは、自分自身がCEOであるべきだと思えないこと。苦闘とは、自分の能力を超えた状況だとわかっていながら、代わりが誰もいないこと。苦闘とは、全員があなたをろくでなしだと思っているのに、誰もあなたをクビにしないこと。苦闘とは、自信喪失が自己嫌悪に変わること。苦闘とは、苦しい話ばかり聞こえて、会話していても相手の声が聞こえないこと。苦闘とは、痛みが消えてほしいと思う時。苦闘とは、不幸である。苦闘とは、気晴らしのために休暇をとって、前より落ち込んでしまうこと。苦闘とは、多くの人たちに囲まれていながら孤独なこと。苦闘は無慈悲である。

苦闘とは、破られた約束と壊れた夢がいっぱいの地。苦闘とは冷汗である。苦闘とは、はらわたが煮えくり返りすぎて血を吐きそうになること。苦闘は失敗ではないが、失敗を起こさせる。特にあなたが弱っているときにはそうだ。弱っているときは必ず。

採用周りの話

今の勤めている会社は100-200人くらいの規模の会社で、そんなに小さくはないが大きくもない会社で、日々採用面接を担当している。 候補者の職歴はスタートアップから大企業から様々で、面接をするたびにこの人は果たしてこの会社でやっていけるだろうかと考えている。 本書は基本的には「幹部」の人事について言及していたが、一般社員にもある程度当てはまるように感じ、とても参考になった。

「大企業の幹部が小さな会社で成功できない理由」のセクション

大企業から小さな会社の転職の場合、

  • ペースのミスマッチ: 「大企業の有能な幹部の多くは、四半期に新しいプロジェクトが3つあったら、多すぎると言うだろう」
  • スキルのミスマッチ: 「大企業を動かしていくには、新たに組織をつくり上げるのとはまったく異なるスキルが必要だ」

のようなミスマッチがあり、そして、そのような採用の失敗を防ぐには、

  • 面接段階でどうしようもないミスマッチを察知する
  • 面接と同じくらい融和を大切にする

が大切だと書かれている。

ミスマッチの検知

下記のような質問をすると良い、と言っており参考になる。

  • 質問: 仕事に就いて最初の一ヶ月に何をしますか?
    • 勉強という答えを強調しすぎる人には要注意だ
    • 候補者が、割り込み駆動型である兆候を見逃してはならない。小さな会社では、割り込みはいつまでたってもやってこないからだ。CEOが無理だと思うくらい新規プロジェクトを持ってくる候補者を探そう。
  • 質問: 新しい仕事は、あなたの今の仕事とどう違いますか?
    • 仕事の違いを自覚しているrかどうかに注目すること
    • 過去の体験の大部分を今すぐ応用できると思い込んでいる候補者は要注意だ
  • 質問: なぜ小さな会社に入ろうと思いましたか?
    • ストックオプションが主要な動機でないかに注意せよ。ゼロの1パーセントはゼロだ。
    • 大きな会社と小さな会社でもっとも大きく違うのは、経営している時間と、創造している時間の長さだ。

入社したら積極的に溶け込ませる

  • 創造を強いる
    • 毎月、毎週、あるいは毎日でも目標を与えて、すぐに結果が出せることを確認する。
  • 「理解」をしているか確認する
    • コンテキストを持たない幹部は、スタートアップでは価値を持たない。
    • できるだけ早く、必要な情報を与えること。何も質問がなければ解雇を検討すべきだ。30日経っても状況を把握できていないと感じたら、間違いなく解雇すべきだ
  • 社員と接触させる

その他の人事の話

下記のセクションも良かった。

  • 人を正しく解雇する方法
  • 幹部を解雇する準備
  • 親友を降格させるとき
  • 友達の会社から採用してもよいか

人間の感情を認めた上で、人事判断を行う際にどう振る舞うのが良いのかを経験を元に描かれていて良い。 このような人事がどのようなダイナミズムで発生するかが面白かった。

「幹部の採用 - 未経験の仕事でも適任者を見つける」のセクション

幹部の採用のためのステップは下記の通りと書かれている。

  1. 自分が欲しいものを知る
  2. 適性を見極めるプロセスを実行する
  3. 孤独な決断を下す

人の採用を行うに際して陥りやすい罠として、「典型的な人物を探してしまう」という項目が挙げられており、最近の自分の判断を省みながら読んだ。 結成したての少数チームのメンバーの採用にとっても、大事な考え方だなと感じた。

もし私が典型的な人物を探していたら、マーク・クラニーを雇うことも、今あなたがこれを読んでいることもなかっただろう。この誤ったアプローチは、セールス責任者にプラトンイデア(真実の姿)を求めることと同じだ。まず、理想的なセールス担当幹部のイメージを描き、次に現実世界の候補者を自分のモデルと一致させようと試みる。これは良くない方法だ。第一に、あなたが雇おうとしているのは、架空の会社で働く概念上の幹部ではない。自分の会社の今この瞬間にとって、正しい人物を雇わなくてはならない。2010年のオラクルのセールストップは、1989年なら失敗していた可能性が高い。アップルのエンジニアリング担当副社長は、フォースクエアにとっては完全に誤った人選かもしれない。重要なのは詳細さと明確さだ。第二に、CEOが描く空想モデルは、ほぼ確実に間違っている。CEOがこのモデルをつくった根拠は何だろうか。最後に、そんな曖昧な基準を面接チームに教え込むことはあり得ないほど困難である。その結果、全員が何か違うものを求めることになる。

目標管理の話

「社員がマネージャーを誤解するとき」のセクション

このセクションでは、数字を重視するあまり間違ったインセンティブを与えてしまい失敗していた事例が書かれていた。

促進する対象には、定量化できるものとできないものがある。定量的な目標についてばかり報告して、定性的な目標を無視していけば、定性的な目標は達成できない-たとえそれがもっと重要な目標であったとしても。純粋に数字だけによるマネジメントは、筋通りに色を塗るぬり絵キットのようなもの-あれはアマチュア専用だ。

と言っている。これは、最近のトレンドの一部の OKR による目標管理に反対になる意見のように見える。 (参考: https://satoshihirose.hateblo.jp/entry/2019/01/05/153343

文章中には見落とされがちな定性的な目標として

  • 対ライバルの勝率は、上がっていたか下がっていたか?
  • 顧客満足度は、上がっていたか下がっていたか?
  • わが社のエンジニアたちは、この製品をどう思っていたのか?

を例としてあげられていたが、社員や顧客に対するサーベイによりこれらの項目を定量化することはOKRの中では普通に行われているように思う。

カルチャーの話

罵倒語

著者のベン・ホロウィッツは罵倒語を良く使うらしく、社内でそれを使うことの是非についても書かれてあった。 部下からその点を問題提起され、ベンは下記のように整理したらしい

  • テクノロジー企業では罵倒語を不快に感じる社員もいるが、不快に感じない社員もいる。
  • もし罵り言葉を禁止したら、罵り言葉を使っている社員は「この会社は古臭い」と思い、辞めてしまうかもしれない。
  • 罵り言葉を続けると、それを不快に感じる社員の一部は辞めてしまうかもしれない。
  • なんといっても私が最大の違反者なのだから、私の判断に偏りが入り込むのは避けられない

この罵倒語を使っている会社はクールである、といった風潮を感じたことはないが、当時のアメリカではそのような風潮はあったのだろうか(今も?)(もしくは、エグゼクティブの界隈ではあったのかもしれない)。 その問題提起に対して、ベンは社員を集めて下記のように言い渡したらしい。印象的な場面なので長いけれど引用する。

「当社における罵倒語の頻繁な使用が一部の社員を不快にしているという問題があると知った。罵倒語の使用頻度のナンバーワンとして、私自身の振る舞いを反省したが、同時に当社全体の問題としても考えてみた。私の考えではふたつの選択肢がある。(a)罵倒語の使用を禁じる、(b)罵倒語の使用を受け入れる-だ。このふたつの中間の選択肢というのは、理論的にはともかく現実的ではない。『罵倒語の使用を最小限に留める』などという対策は実効性がない。前にもの述べたことだが、世界で最優秀の人材を惹きつけられなければ、われわれは勝利できない。テクノロジー業界ではほとんど全員が罵倒語の使用を受け入れている。

そこで、罵倒語の使用を禁じることで生じる人材の損失は、受け入れることによって生じる損失を上回ると予想される。以上の理由で当社は、罵倒語の使用を受け入れる。ただし、いかなる場合でも、セクシャル・ハラスメント、その他の不当な目的で罵倒語を使ってはならない。罵倒語を使うかどうかにかかわらず、かかる行為が許されないのはもちろんだ。(略)」

その日以後、罵倒語に対する苦情は後を断った。それにこの方針によって会社を辞めた人間も出なかったようだ。ここで強調したいのは、組織で問題が生じたときに、必ずしも解決策は必要なく、単に事柄を明確化するだけで良い場合もあるという点だ。脅迫やセクハラにならない限り罵倒語の使用はオーケーだということをはっきりさせただけで、問題は消滅した。ともかく、この方針の結果、職場環境の快適さは維持され、辞職率は低いままにとどまり、苦情はなくなった。つまり、この方針は正しかったということになる。

最近の心理的安全をケアする風潮からするとにわかには信じられない言説だ。 (参考: 「暴言を吐かれた人は処理能力が61%、創造性58%落ちる」暴言が#職場 にもたらす悪影響 https://togetter.com/li/1201526

もしくは、単に私がスタートアップもしくは「戦時の企業」に向いていないだけなのかもしれない。 少なくとも個人的には頻繁に罵倒語やリスペクトに欠ける発言を頻繁に目にするような状況は、退職を検討するひとつの理由になるのではと感じている。

「優秀な人材が最悪の社員になる場合」のセクション

企業が大きくなるにつれて、円滑なコミュニケーションはどんどん難しくなっていく。 それにつれて優秀な人材が悩みのタネとなるという話も興味深かった。

ある種の人々はコミュニケーションのスタイルがあまりに好戦的なので、誰も口を利きたがらなくなる。誰かがマーケティングの話題に触れるたびにマーケティング担当副社長が飛び出してきて怒鳴りまくるようだったら、誰もマーケティングの話をしなくなる。会議にこういう根性曲がりがいると誰も口を開こうとしなくなる。その結果、会社幹部の間での意思疎通に大きな障害が生じ、やがて会社全体にその悪影響が出る。念のために付け加えておくが、問題の社員が一方で極め付けに頭がいい場合のみ、こうした破壊的な影響が広がる。そうでなければ、その社員が誰を攻撃しようと気にする者はない。ケガが大きくなるのは大きな犬に噛まれたときだ。もし幹部スタッフに大きな犬がいる場合は、その犬をどこかに追い払うしかない。

偉大なフットボール・コーチも同様のことに言及していると引用し、その影響をコントロールすることを肝に銘じるよう締めている。

「ひとりがバスに遅れれば、チーム全員が待っていなければならない。うんと遅れれば、チームは試合に間に合わなくなってしまう。だからそんなヤツは許しておくわけにはいかない。バスには出発しなければならない時刻がある。そうは言っても、時にはあまり役に立つので、バスを遅らせるのもやむを得ないような選手もいる。しかしそれはよほどの選手に限る」 (略) 時として会社にはデニス・ロッドマンのようにあまりに貢献が巨大なので、何をしても許容せざるを得ないような社員が存在することがある。だがその場合、CEOはその社員の悪影響が会社のほかの社員にひろがらないように自ら措置を講じなければならない。「社会全体でもデニス・ロッドマンはめったにいない」ことを肝に銘じておくべきだ。

1on1の話

1on1とは書いていなかったが、社員との個人面談の大事さについても言及している。 社員の方が個人面談の主役だと前置きしつつ、意見を引き出すのに役に立つ質問の例として下記のものをあげている。

  • われわれがやり方を改善するとしたらどんな点をどうすればよいと思う?
  • われわれの組織で最大の問題は何だとおもう?またその理由は?
  • この職場で働く上で一番不愉快な点は?
  • この会社で一番頑張って貢献しているのは誰だと思う?誰を一番尊敬する?
  • きみが私だとしたら、どんな改革をしたい?
  • われわれの製品で一番気に入らない点は?
  • われわれがチャンスを逃しているとしたら、それはどんな点だろう?
  • われわれが本来やっていなければならないのに、やっていないのはどんなことだろう?
  • この会社で働くのは楽しい?

まとめ

紹介しなかったどの章も興味深く、組織で働く上で折に触れて読み返したいと思えるような本だった。

クリスティーナ・ウォドキー「OKR」とジョン・ドーア「Measure What Matters」を読んだ

What's this?

去年の8月に現職に就いた際に、組織目標をOKRで管理していることを知りました。 OKRについてのインターネット上の情報などを調べていくうちに、「シンプルかつ具体的で少数の重要な目標に絞る」「野心的な目標を挙げることで成果をストレッチさせる」などのコンセプトが気に入り、その詳細な思想や運用について興味が湧きました。 そこで、クリスティーナ・ウォドキー「OKR」とジョン・ドーアMeasure What Matters」の二冊を読んだので、学んだ点をまとめます。

OKRそのものの概要は以下の記事などを参照してください。

OKRと過去のMBOとの違い

1960年代にはドラッカーが「目標による管理(MBO)」を提唱し各社が導入をしていたそうですが、下記の理由などにより徐々に形骸化していってしまったそうです。

  • 目標の中央集権化による、伝達の遅延
  • 頻繁に更新しないため、状況の停滞
  • 給与や賞与と連動させたことによって、社員がリスクを取らなくなってしまった

これらの問題を受けてOKRは、

  • ボトムアップによる目標管理の推奨
  • 四半期ごとの更新、定期的な見直し
  • 給与や賞与と連動させない

などを基本姿勢としています。

品質保証について

数値目標と品質目標

野心的な目標は得てして品質を犠牲にするので、ジョン・ドーアは数値目標と品質目標を対にする運用も紹介しています。 例えば、新機能を3つリリースするという数値目標に対して、品質保証テストで各機能あたりのバグは5つ以下にする品質目標を対にするなどの例を挙げています。

健康・健全性指標

似たような問題意識があるからか、クリスティーナ・ウォドキーは健康・健全性指標を紹介しています。 ObjectivesとKey Resultsとは別に、失ってはいけないこと、守らなくてはいけないことを、状況に応じて赤色・黄色・青色など適宜評価し続ける方法です。 顧客との関係、コードの安定性、チームの健康などを例に挙げています。

勤務評定と目標管理の関係

OKRの達成度により勤務評定を下すことは、アンチパターンであることをジョン・ドーアは繰り返し述べています。 年に一度の勤務評定の代わりに、下記の継続的パフォーマンス管理(CFR)の導入を推奨しています。

  • 対話(Conversation): パフォーマンス向上を目的に、マネージャとコントリビュータのあいだで行われる真摯で深みのある意見交換。
  • フィードバック(Feedback): プロセスを評価し、将来の改善につなげるための、同僚との双方向あるいはネットワーク型のコミュニケーション
  • 承認(Recognition): 大小さまざまな貢献に対して、しかるべき個人に感謝を伝える。

一方で、勤務評定を目標管理から完全に切り離すべきとまでは主張しておらず、OKRで数字で進捗を測れることの意義についても述べています。

ただし、勤務評定と目標管理は完全に切り離せるとか、切り離すべきだと言っているわけではない。 個人が「何を達成したか」をデータでわかりやすく示すことには、勤務評定の歪みを防ぐ効果もある。 しかもOKRは個人が取り組んでいる最も価値のある仕事を示しているので、次の評価サイクルで信頼性のあるフィードバックをするための材料となる。 ただ、目標が報酬を決めるベースとなったり、悪用されたりすると、従業員は実力を隠すようになる。 守りに入り、驚異的成果を目指して限界に挑戦しなくなる。 そして仕事のやりがいのなさにうんざりする。最も割りを食うのは組織だ。

進捗と評点の違い

通常OKRは目標達成度合いに応じて0.0-1.0の評点が付きますが、ジョン・ドーアは、進捗と評点は厳密に一致する必要はない旨を記しています。

たとえばチームの目標が新規顧客の獲得で、あなた個人の「主要な結果」が50本の電話をかけることだったとする。 結局あなたが見込み客にかけた電話は35本で、そのまま採点すると70%になる。 これは成功だろうか、失敗だろうか。 データだけではよくわからない。 だがかけた電話のうち、10本以上がそれぞれ数時間続き、結局8件の新規顧客を獲得できたら、評価は最高の1.0としてもいいかもしれない。 反対に電話をかけるのをグズグズ先延ばしし、期限間際に35本の電話をかけ、新規顧客は1件しか獲得できなかったなら、パフォーマンスの自己評価は0.25となるかもしれない。 もっと努力できたはずなのだから。

その上で、行動の振り返りが大事であると主張しています。

OKRは本来、行動を重視する。しかしやみくもに行動するだけでは、回し車のハムスターと変わらず、単なる苦行になってしまう。 充足感を得るためにカギとなるのは、野心的な目標を立て、そのほとんどを達成し、足を止めてそれを振り返り、そのうえで新たなサイクルを繰り返すことだ。

コミットする目標と野心的な目標

「Measure What Matters」では巻末にGoogleでのOKRの具体的な運用方法についての記載があり、参考になります。 例えば、GoogleではOKR上で、コミットする目標と野心的な目標を分けて管理しているそうです。 内容としては、

  • コミットする目標は、プロダクトのリリース、経営指標などと結びついている期限内に100%達成する必要がある目標
  • 野心的な目標は、壮大なビジョン、高いリスク、未来志向の発想を反映した目標

という感じで分けて運用しているようです。 それらの区別をつけないことで、コミットすべき目標の未達率が高まる、チームが守りに入るなどの弊害が出ると述べています。

良いOKRのためのリトマステスト

こちらもGoogleの運用例中の記載があった内容ですが、良いOKRを判定するための自問すべき項目が挙げられています。 具体的には下記のような内容です。 全ての項目をクリアする必要はないとは思いますが、OKRを設定する際の参考になります。

  • そのOKRは合理的に考えて、1.0の評定を得ても、エンドユーザーへの直接的価値あるいは経済的恩恵をもたらさない可能性があるか
  • すべてのKRで1.0の評定を得ても、目標の意図が達成されない可能性があるか
  • そのOKRを書くのに5分もかからなかったら、おそらく良いものではない
  • 目標が一行に収まっていないなら、十分簡潔とは言えない
  • KRにチーム内でしか通じない用語が含まれていたら、おそらく良いものではない
  • 具体的日付を使う。すべてのKRの期日が四半期の最終日となっているのは、まともな計画がない証拠だろう
  • 「主要な結果」は必ず測定可能なものにする
  • 指標に曖昧さがないこと
  • OKRに含まれていないが、チームにとって重要な活動(あるいは活動の一部)があれば、OKRを追加する
  • 規模が大きい組織ではOKRを階層式にする

豊富な事例紹介

「OKR」の前半は、架空のスタートアップがOKRを活用したおかげで成功する物語、「Measure What Matters」はインタビュー形式でOKRを導入している各社の活用事例が載っていて、その運用が具体的でわかりやすく紹介されています。

特に、「Measure What Matters」の中で、OKRの父アンディ・グローブインテルにて、8086がモトローラの68000にいかにして対応したかの章が、具体的で臨場感があり面白かったです。 その他にも、

  • ビル&メリンダ・ゲイツ財団のケーススタディ(インタビュー中に具体的に挙げられたOKRは基本原則に即していないように見えますが...)
  • スンダー・ピチャイのインタービューによる、グーグルクロームチームのケーススタディ
  • ユーチューブCEOスーザン・ウォジスキのインタビューによる、ユーチューブのケーススタディ
  • U2のボノのインタービューによる、途上国援助活動ONEのケーススタディ

などが興味深かったです。

個人での運用するとしたら

OKRは組織的に導入することを目的として設計されていますが、個人で使用しても一定の効果はあるでしょう。 会社の評価と関係のない自分個人の四半期目標を用意すると下記のような感じになるでしょうか。

Objectives:

  • エンジニアとして国際的に活躍する、またはそのための下地を作る

Key Results:

  • 週5で30分のオンライン英会話レッスンを受ける
  • TOEFLで100点を取得する
  • international conference で業務で行った活動についての発表を1回する
  • 使用しているOSSにPull Requestを4回投げる

その他

気分を盛り上げる言葉たち

  • 「目的地がどこであるか知らなければ、そこにたどりつくことはできないだろう」- ヨギ・ベラ
  • 「アイデアを思いつくのは簡単。実行がすべてだ」- ジョン・ドーア
  • 「人の真価は、どのような能力があるかより、どのような選択をするかでわかる」 - J・K・ローリング
  • 「最善を善の敵にしてはならない」 - ヴォルテール

アンディ・グローブスタンフォードでの講義動画