【書評】INSPIRED 熱狂させる製品を生み出すプロダクトマネジメント
読書感想文です。
前書き
先日参加したイベントで紹介されていた本です。 ryu-rand.hatenablog.com
自分はいま少人数チームでリードエンジニアを務めているので、実装以外にもいろいろとやります。 その中でも、自分たちの製品はどんな課題を解決するのか、自分たちはチームとしてどこを目指していくのか、などの見定めや定義づけはまだまだ試行錯誤中です。 そのあたりの参考になればと思ったのがこの本を読んだきっかけです。
雑感
プロダクトマネージャーの考え方、やるべきことがやや抽象的に書かれています。 技術的な視点では書かれていないので、非エンジニアでも問題なく読めると思います。 ポイントポイントで参考になる点がありましたが、同じ内容の繰り返しもあり、記述はやや冗長かなという印象です。 チャプター名が具体的に書かれているので、気になる箇所をピックアップして読むのがおすすめです。
グッときたところ
プロダクトマネージャーカンファレンス 2019 に参加してきた
こんちは、じぬ(@reximology)です。
プロダクトマネージャーカンファレンス 2019 というイベントに参加してきました。
参加の動機
現在は少人数チームで開発を行っているため、エンジニアでもこういったプロジェクト開発を推し進める知見を持っていた方がスムーズに進めていけるのでは、というところから参加を決めました。 余談ですが、プロダクトマネージャーは PdM と略す場合が多いようです。(PM と記載してる例もありましたが
参加したセッション
day1
- 作り手の想いとユーザーをつなぐための悪戦苦闘
- ピクシブのプロダクトマネージャー、自己組織化されたチーム、チームを支える組織
- “失敗事例で学ぶ” 失敗しないプロダクトマネジメント- PMの必須スキルと、自走する組織のつくりかた -
- テクニカルアドバイザー 及川卓也 × エン・ジャパン 公開トーク- PMの必須スキルと、自走する組織の裏側 -
- 企業が求めるプロダクトマネージャーとその人材戦略
- 異動、転社・転職などを活用したプロダクトマネージャーのキャリア戦略
day2
- 変化の激しいプロジェクトを成功に導くには - 会議ドリブンなプロジェクトマネジメントメソッド
- 打席に立ち続けられるプロダクトチームを作ろう
- プロダクトアウトな新規事業立ち上げのリアル
- PMはどのようにバリューを出していくのか
- プロダクトマネージャーが知っておくべき、「OKR」を通じたこれからのチームマネジメント
- シリコンバレーPMのキャリアと働き方
作り手の想いとユーザーをつなぐための悪戦苦闘
要約
- 去年はリリース前後の話をした、今年はリリース後の課題
- 市場が伸びるとともに自分たちの想像できていないユーザが現れ始めた
- 自分たちがユーザを理解できているという過信があった
- ユーザと向き合う対話を徹底した
- 各メンバーにもその視点を浸透させた
グッときたところ
- 例えばライフサイクルの異なる地方ユーザなど、具体的に開発者が想像できていなかったユーザの登場
- ユーザ属性をカテゴライズし、確実に見分ける質問を必ずアンケートで実施
- ギルドワークス社のワークショップなど、チーム全員でユーザと対話する訓練
- Monthly Release Sharing という社内知見共有の定例化、マネージャーではなく現場のメンバーが話すことを徹底
ピクシブのプロダクトマネージャー、自己組織化されたチーム、チームを支える組織
要約
- pixiv で PdM が持つべきスキルとスタンスをまとめた PM コンピテンシーを整備
- プロダクトマネジメントトライアングルを実際の PdM でどうまとめるか
- 複数の PdM を抱えるチームという事例
- 既存のエンジニア組織を参考に、PdM を組織化
- ドメイン知識を高いレベルで獲得し、メンバーお互いを尊重し合える文化
グッときたところ
- 企業によっても異なる PdM の定義を言語化した
- 参考にしたエンジニアギルドが pixiv 内で10年近く続いた組織である
- プロダクトのあるべき姿を定義、チーム力を最大化、という二つの PdM のミッション
- PdM の育成に再現性を持たせるための試行錯誤
“失敗事例で学ぶ” 失敗しないプロダクトマネジメント- PMの必須スキルと、自走する組織のつくりかた -
要約
- 相次ぐ退職、属人化によって新規プロジェクトに取り組めないなどの重い課題
- PdM を明確に定義できていない
- Inspired 読んだ、社外のスペシャリスト(及川さん)に相談した、GAFA などの事例を調査した
- PdM の役割、持つべきスキルを明確化した
グッときたところ
- 役割:製品価値を最大化し、それに向けてチームをつないでいく
- 価値とチームという2軸は上述の pixiv と近い(おそらく PdM に期待される共通項
- 期待されるスキル:創る能力、広める能力(製品のグロースや、ドメイン知識の理解など
- 能力値をレベルで表現することで各 PdM の強み弱みを明確化した
テクニカルアドバイザー 及川卓也 × エン・ジャパン 公開トーク- PMの必須スキルと、自走する組織の裏側 -
要約
- 正直各社の取り組みは似ている
- PdM トライアングルは難しい、それぞれかみ砕いていく必要がある
- その上でメンバーが自分たちの物である、という認識を持つように促していく
- PdM は全体に精通する必要がある
グッときたところ
- レベルごとの期待値をリスト化するのは大変だったが、ネット情報をはじめとして洗った上でかみ砕いていった
- 非エンジニアの PdM は SQL とかとっつきやすい、構文がわかりやすく結果も見える、そういうところから全体に精通する必要がある
- スキル表はまだ評価に組み込めてはいない、それよりもキャリア相談などに活かしている
- PdM は最終的に全てに責任がある、自分でやるのは一つの手段だが、できるメンバーに頼るのも必要なスキル
企業が求めるプロダクトマネージャーとその人材戦略
要約
- PdM 人材の需要
- オンボーディング
- 価値観醸成
グッときたところ
- PdM のタイプは様々、多様性が企業の強みになる
- 会社の文化を理解すること、創業者との信頼関係があることが求められる
- メンバーは権限では動かない、信頼関係の構築が必要になる
- 新しい PdM に丸投げは絶対ダメ
- 最初は花を持たせて成功させる、そこまでやってようやく採用が成功と言える
- 会社からの期待値をきちんと伝える
- ミニ CEO とも呼ばれる、視野視座はきちんと会社と揃えていく必要がある
- 既存の PdM と 1on1 などを通して併走させ、きちんと会社になじませる
異動、転社・転職などを活用したプロダクトマネージャーのキャリア戦略
要約
- PdM に必要なスキル
- PdM の採用
- 年収の相場
- PdM を目指した転職
グッときたところ
- PdM に必要なのは、大雑把に言えば「なんとかする力」「突破力」
- 足りない力があればそれをどうやって乗り越えるのか、を考え実行できる人
- PdM は統合スキルが求められる
- 鳴り物入りで入社した人でもダメなことはある、むしろ新卒上がりの方が強かったりする
- 中途の人にはいきなりポジションを与えず、補佐みたいなところから周りの信頼を得て成果を出せるか見る
- 期待値を下げて、きちんと動ける環境を用意してあげる
- 最初から役職にこだわる人は難しい
- News Picks だとトップクラスは年収2000万くらい
- 一つのプロダクトを完全に任せられる人材なら1000万くらい
- PdM を目指すには、とにかくユーザ満足度を考え、それを科学する(トライ&エラー、デザイン思考
- ユーザリサーチのスキル(インタビュー力、ユーザの思いを察する力
- 業界リサーチ(ヒットの規模を見る
ネットワーキング
初日の夜はネットワーキングがありました。 自分は OKR のテーブルで話をしたのと、pixiv ブースでセッションの詳細を話してきました。
OKR
- OKR は仮説検証の手法、達成できなければ方法が悪い
- 達成しても O が満たせないなら KR が悪い、ならば何を持って成果を評価できるか再検討する
- 組織規模によっては OKR 自体を PdM が定める(組織の OKR に沿った内容を見定める
- 個人レベルの OKR まで定めても、組織のコピペになるのであまり効率が良くない、という印象がある
pixiv
- エンジニアギルドは10年以上続いた取り組みに最近名前がついた
- 組織自体に拡張性を持たせ、属人化せずに取り組みが回っていくように考えた結果いまの構造になった
- エンジニア、デザイナーとしての良い取り組みが回り回って評価にも繋がっていく
- やってダメなら見直す、というアジャイル的思考で進めた
- 仕組み化にこだわることで続いてきた
変化の激しいプロジェクトを成功に導くには - 会議ドリブンなプロジェクトマネジメントメソッド
要約
- プロジェクトマネジメントは「仕事を動かす仕事」
- 会議をどう活用できるか
- メンバー全員でアジェンダを作る(みんなで決めること決めないことを話す
- 課題を持ち帰らず必ず次のタスクを決める(みんなで納得して次に進む
グッときたところ
- 会議が悪いわけではないが、今ままでの会議形態は変化が激しい、アジャイル、などの時代に合ってはいない
- 話す内容からして全員できちんと話す、他人事にしない
- 会議で話すのは前回からの進捗、プロジェクト全体の方向性、次回までの予定
- タイマーとかアジェンダを表示する2台目のモニターがあるとはかどる
打席に立ち続けられるプロダクトチームを作ろう
要約
- MOV というサービスだけでもいろんな取り組みがある
- 変化が激しい中でも高い打率を保つ
- DeNA における PdM の役割を定義(フィロソフィー
- 専門職として評価・育成するための組織化
- 失敗できる環境の提供
グッときたところ
- PdM は作る物を決める仕事だが、UX リサーチャーやデータサイエンティストとの連携が不可欠
- プロダクト価値を明確化し、狙った市場に届けられる人材が必要
- エンジニアやデザイナーと専門用語で会話できる知識
- 社内のスペシャリストとともに働くことでいち早く PdM としての経験を積める
- 完璧な人はいない、PdM としての得意な部分を伸ばしていく
プロダクトアウトな新規事業立ち上げのリアル
要約
- PdM のやるべきことは会社のフェーズによって異なる
- CPO のポエムから生まれた機能開発
- 時期によっては属人化を許容したり、徹底的に仕組み化したり
- PdM は最も価値がある(レバレッジが効く)ことをやる
グッときたところ
- PdM の仕事は多様、ベースとなるフレームはあってもそれだけでは解決できない
- いかに多くの事例を知っているか、が強みになる
- プロダクトアウトで生まれた機能をいかにビジネスにするか、も PdM が対応した
- プロダクトで解決するのか運用で解決するのか、俯瞰的に決めていく
- ローンチ直後は強いメンバーで乗り切った、あえて属人化した
- 規模が拡大してそれではまわらなくなった時にようやく仕組み化した
- 仕組み化は得意なメンバーをアサインして徹底的に対応
- クライアントの初期導入が悪かったので、まずは Onboarding から整えた
- PdM の仕事はプロダクト開発、セールス、CS の仕組み化、チームビルディング、と多岐にわたる
- 価値の最大化のための仕事を選ぶ
- 自ら解決しつつ、別の手段も常に考えておく
PMはどのようにバリューを出していくのか
要約
- PdM が必要になるのは迷いが生まれやすい新規開発や、ビジネス要件の強いプロジェクト
- ユースケースに精通するのは CS、だからこそ開発と CS は密に連携が必要
- PdM はマーケットやクライアント課題を特に理解しておく、なるべく第一情報に触れていく
- 無理なことはできるメンバーに頼る
グッときたところ
- 開発と CS 連携の重要性
- タスクは共有や管理のためすぐに issue 化する、ただしやらないなら閉じる(必要な過大ならいずれまた上がってくる
- 深い知見を無理に追うのではなくメンバーに頼る(これは他社でも述べられている
- チームビルディングに必要なのは権限譲渡、そして長期目線を共有すること
- メンバーが給料を気にせず働けるのが理想、そのためには希望の少し上を払い続けられれば理想(まだ試行錯誤中
- 小さいチームの時は特にミッションを意識し、みんなでどこを目指しているのかを認識合わせする
プロダクトマネージャーが知っておくべき、「OKR」を通じたこれからのチームマネジメント
要約
- Google の基本はボトムアップ
- 近年:プロダクトではなくプラットフォームを作る、オープン主義、エンプロイーエクスペリエンス、走りながら考える学習主義
- OKR は途中で変えてもいい、ただし日々の観測が重要
- 組織から社員まで一貫性を持たせる
- 内容を開示し、1on1 を通して習慣化する
グッときたところ
- OKR は目標から逆算する、ストレッチな目標にすることで挑戦する文化を創る
- OKR は個人の夢でもいい、それを組織に紐付けていく
- 導入目的が曖昧、経営陣の足並みが揃ってない、マネージャーが組織 OKR を理解してない、などが失敗あるある
- OKR は最大のインパクトを与える物に絞る
- 仕事を通した自己実現を是非考えてほしい
シリコンバレーPMのキャリアと働き方
要約
- シリコンバレーの方がデータドリブンは強い
- 会社がトレーニングを提供したり、横のコミュニティも強い
- PdM から先のキャリアパスは結構なんでもある
- PdM をやるなら何が得意か、何が好きかはきちんと整理しておく
グッときたところ
- シリコンバレーだと PdM 採用で SQL 書いたりする。それくらいデータに触れるのは日常
- アルムナイ組織と呼ばれる企業の卒業生コミュニティもつながりが強い
- 改善案を効果は別にしてもたくさん思いつける能力が必要
- 自分が使うアプリの改善案を出せる、またそれの否定案も出せる能力
- 企業によって PdM の出自は違う(Airbnb だとデザイナー系が多い
- 日本だとまだまだ逆境も多いが、小さく成功して実績を作るのが良いと思う
- 常にカスタマーという存在を意識する
感想
共通内容として感じたこと、まず PdM のミッション。
- 製品価値を上げること
- それを完成させるチームを作ること
その上で、以下の特性がある。
- PdM はプロダクトの最終責任を担う
- マーケット、ドメイン知識、チームメンバーのこと、企業文化、など関わる全てを知る必要がある
- できないことはメンバーに頼ることも必要になる
- あらゆる手段を講じて課題を解決することが期待される
各社で PdM に求める役割やスキル、育成、評価、採用の方法は手探りな状態という印象だった。 それでもデータドリブンや現場の知識など、エンジニアから PdM の働きをするとっかかりが多いということを知れたのは収穫だった。
その他の発表
こちらのブログでまとめてくださっています。 takaking22.com
【書評】エンジニアのためのマネジメントキャリアパス ―テックリードからCTOまでマネジメントスキル向上ガイド
読書感想文です。
雑感
メンターから技術戦略担当まで、マネジメントに関わる仕事のノウハウが管理対象の規模に応じて段階ごとに書かれている。 著者の体験に基づいているため事例がイメージしやすく、翻訳が良いこともあってかなり読みやすかった。
グッときたところ
コミュニケーションについて
- 一度で伝わると思わない。分かってくれると思わない。本当に伝えたいことは粘り強く分かるように何度でも伝える。
- 業務外の内容も含め、お互いにコミュニケーションをするのが当たり前な空気感を日頃から醸成していく。
- メンティー相手の場合など、自分が目上の場合は特に自分からコンタクトを心がける。
- 評価を伝える際にはきちんと長所も時間をとって伝える。
チーム作りについて
- ルール化や仕組み化も手段の一つ、こだわりすぎない。何を解決したいのかを常に意識する。
- コードレビューや技術選定の基準などを明確に言語化することで、チームでのやりとりや将来の引き継ぎもやりやすくなる。
- チームが見積もりや開発をできるような土台は自ら作り上げる。
- 部下に対して定期的継続的にフィードバックを行う。それを実現するために常に部下を観察し長所や短所を理解する。
リーダーの仕事について
EOF2019 に参加してきた
こんにちは、じぬ(@reximology)です。
EOF 2019 というイベントに参加してきました。これはエンジニアリングマネジメントを主題としたカンファレンスです。 eof.connpass.com
ここでは参加したセッションと、特に印象深かった点をまとめます。
はじめに
参加したセッションは次の通りです。(タイムスケジュール)
- Opening Session 私たちの現在地点、向かうべきところ。
- 正しいものを正しくともにつくる
- 筋肉質なエンジニア組織を目指して ~失敗と成功から学ぶエンジニア組織の作り方~
- 社員100人規模のWebサービスにおけるエンジニアリングマネジメント
- エンジニア採用どうしてる? 〜エンジニアのトップが語る、2019年の採用活動の本音〜
- 持続可能なエンジニア組織のデザインに必要な事とは
- 学習する組織の作り方
- DMM改革の1年、その実際と反省
Opening Session 私たちの現在地点、向かうべきところ。
本イベントの概要説明などです。カンファレンスの価値は廊下にある(セッション後に参加者同士でコミュニケーションがとれる場があること)という話をしてました。そこに繋がる流れで隣の人となんかしゃべってくださいはびっくりしましたが、どうにか自己紹介して交流できました。
正しいものを正しくともにつくる
カイゼン・ジャーニーの著者である市谷 聡啓さんのセッション。
www.slideshare.net
https://www.amazon.co.jp/dp/B078HZKLMBwww.amazon.co.jp
要約
- 不確実性の多い序盤は選択肢を広くとり、仮説検証を繰り返してモデル化することで選択肢を狭めていく
- 検証をすべて PO に任せるのは間違い、チーム全体(の各個人)で取り組むべき
- 仮説検証に応じた思考を可視化し、みんなで意見を言い合える環境にする
- 検証に応じて可変性のある、スプリントと機能完成の中間となるマイル(ジャーニー)の提案
グッときたところ
- 不確実性があるのは当然、それへの対処を明確に言語化されているのは非常に参考になる
- スプリントという決まった大きさの物を仮説検証に合わせて柔軟に対応するため、ジャーニーという新しい単位の提案
筋肉質なエンジニア組織を目指して ~失敗と成功から学ぶエンジニア組織の作り方~
要約
- 表題の通り、自らの失敗を交えた取り組みの紹介
- Cheif EM になったが、現場エンジニアのマインドのままだったことが失敗の大きな原因
- 変化を恐れず、これまでの積み上げに頼らず、常に自分をアップデートするという覚悟
- 部長との対話を増やし、事業部の要望に応えつつ組織の成長を担うのが EM の仕事
グッときたところ
- 1on1 にてもうあとがないですよ、と言われてしまうほどに自分の強烈な失敗の話をしてくれたのは衝撃だった
- エンジニアと EM ではマインドから違うという点、それを認識してきちんと自身を対応させたのは見習うべき
- 組織はリーダーの器以上にはならない、だからこそ自分を伸ばす必要がある、という金言
社員100人規模のWebサービスにおけるエンジニアリングマネジメント
要約
- スタートアップ初期、10人〜30人、それ以上、というフェーズごとに CTO として何をしてきたのか
- スピード優先で開発してきたことによる負債や、人数増加によって手が回らなくなる、など正に成長する企業がぶつかる壁の話
- 30人を超えると CEO から権限を委譲させた、スケールのために自分も CTO を交代した
- マネージメントとはいろんな課題を整理してなんとかする、そもそもが曖昧な仕事である
- 他人の給料が上げられるのならその人は配分をもらう資格がある、というのがマネージャーの給料が高いことに対する解釈
グッときたところ
- 情シスの設置や企業の社会的責任など、企業が大きくなるにつれて現れる課題の話は新鮮だった
- マネージャー育成の話は、自分がいま読んでいる本と通じる箇所も多く参考になった
- 技術力が高いとは限らないマネージャーの価値とは何か、が給料面の解釈含めてきちんと言語化されていた
エンジニア採用どうしてる? 〜エンジニアのトップが語る、2019年の採用活動の本音〜
要約
- Classi, Repro という二社での採用に繋がる取り組みの話
- 優れたエンジニアは面白い課題を求める、会社としてそれを提供するためには数年先を見据えた取り組みをきちんと検討しておくことが必要
- 採用に関してはとにかくタッチポイントを増やしている、社内イベントに呼んだり技術相談の場を作ってみたり
- 採用面接やリファラルに向けて社内への取り組みも重要、面接に出られるようにするための研修をする会社もある。Repro では毎週役員レベルの会議に関して議事録公開や質問受付の時間を設けている
- 採用がうまくいかなかった場合、行った会社の人とコンタクトできるなら答え合わせ(なぜ採用できなかったか)をすることもある
グッときたところ
- 自身もリファラルを考えたときにいまいちうまくイメージできなかった経験がある。なのでそれに向けて社内への呼びかけや情報伝達を行う取り組みは良いと感じた。
- カンファレンスのスポンサーになるなど、採用に対して効果はあるが測定できない、そのため予算を確定しにくい、というのはたしかにと思った。
持続可能なエンジニア組織のデザインに必要な事とは
要約
- 創業から5年間の取り組み(エンジニアが0人から40人になるまで)
- 初期は知名度皆無、なのでエージェントを訪問してその場で一緒に書類審査するなど、泥臭く信頼関係を構築していった
- 運営を引き継ぐという事業の特性上、開発者の暗黙知は消滅を避けられない
- どんなに人手不足でも新規入場者をすぐ高負荷なチームには入れることはしないという決断
- ビジネスが安定してくることによるチャレンジの不足、それによるシニア技術者の不満
グッときたところ
- エージェントとの信頼関係、と言う観点
- チャレンジ不足と言う不満は現場からは「でも事業的に仕方が無いですよね」、という形で現れていた
- 実際に不満があるかどうかよりも、事業が成功した結果チャレンジが不要な物であると捉えられていることこそが大きな問題
- 成長した先には光があるべき、シニアが憧れを受ける組織であるべき
- 事業貢献と技術的チャレンジは双方が必須、前者がなければそもそも価値を満たせないが、後者がなければエンジニアの時間を切り売りしているだけ
学習する組織の作り方
要約
- フロント技術だけで見ても学習するべきことはどんどん増えている、時間は無限に必要
- 書籍「学習する組織」から学んだ五つの観点:自己マスタリー、共有ビジョン、システム思考、メンタルモデル、チーム学習
- 毎週作った物を見せ合う場を用意し、アウトプットとそのためのインプットの仕組み化
- 社内 ISUCON やハッカソンによる体感を通した学習
- 自分がエンジニアコミュニティで経験してきたことを社内で再現した
グッときたところ
- 製品版にはセキュリティなど様々な制約が必要
- それを外した状態でどこまで速度が出るのか、どんな品質で動くのか、という体感を通して理想を得る
- 自身がエンジニアコミュニティで得た体験を社内で再現するという観点は新鮮だった
DMM改革の1年、その実際と反省
要約
- エンジニア/デザイナーだけで1000人近い組織をテックカンパニー化することが与えられたミッション
- マネジメント観点以外にも様々な視点(売り上げを元に並べてみたりやサークルやチャット上の交流など横のつながり)で組織構造を眺め、その結果からまんべんなくヒアリングを実施して現状を把握した
- 課題、その課題に対する現場がどんなフィルターを通して解釈しているか、そこから逆算して現場の状況や目的はどうか、を理解していく
- ミッションやビジョンを通して現場における行動までを説明できる一貫したストーリーを整える、それが企業における文化
- 情報公開の場所やタイミングを統一してアクセスを容易にし、自分からの発信とインタビューなど外部を通した発信を重ねることで情報に浸透圧をかけていく
グッときたところ
- 正しく組織をハックするといった内容
- 様々な視点を通した組織の俯瞰、偏りのないヒアリング対象の選定
- トップにあるミッションからそれを具現化していく戦術までの一貫したストーリー
- 自分からの発信をどうやって現場に浸透させるかまでの具体的な見通し
- ヒアリングを通して見えた課題の解消をパッケージ化し、少しずつ現場に浸透させてその成功を認めることで各メンバーに理想的振る舞いのメンタルモデルができていく、それこそが企業の文化
- 何気ない会話の中にミッションが混ざる、例えば slack のスタンプにそれが垣間見える、それこそが文化の浸透
振り返り
イベントに関して
弊社も含めてやはり課題は似たようなものが多いんだなと言う印象です。組織規模の拡大によって無視できなくなる技術負債、エンジニア採用、継続的なチャレンジ課題、文化醸成、などなど。 マネジメントやチーム作りは新卒やよほどの技術スペシャリストでなければ一度はぶつかる課題かと思うので、エンジニアならたいていの人にとって身近な話題だと思います。 それだけに講演を聴くだけでもいろいろと学んだ気になることが多いです。ですがそこでとどめず、自分の振る舞いや自分のチームにおいてどう活かすか、まで踏み込めると正しく血肉にできるのかな、と感じます(自戒)。
その他の講演
聞けなかった講演の資料をまとめようと思ってましたが、こちらでまとめてくださってたので貼っておきます。 medium.com
Unity 内部のコンパイラを追ってみた
こんにちは、じぬ(@reximology)です。
これは Unity ゆるふわサマーアドベントカレンダー 2019 #ゆるふわアドカレ 24 日目の記事です。
この記事では、Unity 用 SDK 開発において遭遇したトラブルと、その解決に至るまでを紹介します。 ※ 本記事は調査手順をなぞりたいと思い時系列で記述していますが、結論だけ知りたい方は解決をご覧ください。
背景
自分は AROW というサービスの開発を行っており、その一部として Unity 用 SDK を提供しています。 SDK は、開発者登録を行うだけでなんと無料で使うことができます(これは宣伝)。
さて、SDK は提供時に DLL 化を行っています。今回紹介するのは、DLL に関連する話です。
開発環境は次のとおりです。(両刀使いのため時々 Windows が出てきますがメインは Mac です)
- macOS HighSierra 10.13.6
- Unity 2017.4.25
- .NET 4.6
DLL の作り方
GUI から作ることもできますが、自動化したかったので CLI で作成することを考えます。
ドキュメント を確認し、mcs
というコマンドを使うことを確認しました。
(当時どのようにインストールしたかは不明ですが、おそらく brew install mono
で環境を構築したかと思います)
実際に DLL を作成するには、以下のようなコマンドを実行しています。(一部省略)
mcs \ -r:$(UnityDirectory)/Contents/Managed/UnityEngine.dll \ -target:library \ -recurse:'Assets/Scripts/*.cs' \ -out:AROW.dll
この場合だと UnityEditor への参照は含まれていないため、Editor 用が必要な場合はもう一つ DLL を作成する必要があります。
問題
エラーとその調査
string str = "abc.def"; string[] ary = str.Split('.'); // ここでエラー!
問題になったのは、string.Split
です。これは引数の文字で文字列を区切り、配列で返すメソッドです。
開発中、つまり Unity で検証している際にエラーはありませんでした。
しかし、上記処理を含むファイルを DLL 内部に含むと、次のエラーが発生するようになりました。
MissingMethodException: Method 'string.Split' not found.
正直、このエラーだけではなにが問題なのか判断できません。 同じ処理を Unity 2018 で実行した場合、もう少し詳細なエラーが確認できます。
MissingMethodException: string[] string.Split(char,System.StringSplitOptions)
コールしたいのは char
を一つ受け取るメソッドですが、エラーが出ているのは別にオーバーロードされたメソッドのようです。
まず、このメソッドについてドキュメントを確認します。
使おうとしているのはこちらです。
params char[]
と宣言されているため、引数が char
一つでも該当します。
逆に、エラーが示すような引数のメソッドは定義されていません。
試しに、リフレクションでメソッドを出力してみます。
// string の関数を出力 var s = ""; foreach (var v in typeof(string).GetMethods(BindingFlags.Public | BindingFlags.Instance)) { s += v.ToString(); s += "\n"; } Console.WriteLine(s);
// Unity での実行結果 ... System.String[] Split(Char[]) System.String[] Split(Char[], Int32) System.String[] Split(Char[], System.StringSplitOptions) System.String[] Split(Char[], Int32, System.StringSplitOptions) System.String[] Split(System.String[], System.StringSplitOptions) System.String[] Split(System.String[], Int32, System.StringSplitOptions) ...
やはり、エラーとなっている char, System.StringSplitOptions
を引数にとる string.Split
は見当たりません。
コンパイラ
ところが改めてドキュメントを見直すと、.NET Core にはこのメソッドが存在しています。
しかも第二引数にデフォルト値が宣言されているため、char
一つを受け取る場合は本来呼び出したかったものと見分けがつかないはずです。
つまり、「自分たちとしては params char[]
のメソッドを呼び出したかったが、コンパイルされる時点では char, System.StringSplitOptions
のメソッドに対して紐付けが行われた。
ただし実行時にはそのメソッドは存在しないためエラーが発生している。」と推測できます。
開発中も DLL 利用時も、実行しているのは Unity であるため共通のはずです。
となると、DLL を作成した際に利用した mcs
に原因があると考えられます。
普段の Unity が利用しているコンパイラはどこにあるのでしょうか。
Unity 内で mcs に関する検索を行うと、次の場所でそれらしきファイルが発見できます。
bin には mcs、lib には mcs.exe が存在します。
// Mac /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/mcs // Windows だと少しパスが異なる /c/Program\ Files/Unity/Hub/Editor/2017.4.25f1/Editor/Data/MonoBleedingEdge/bin/mcs
おそらくこれなんだろうなと思いつつ、Unity から利用されている確証がほしいところです。 そこでこんな実験をします。
Unity が使っているとおぼしきファイルをリネームしました。 後述しますが、ファイル名を変えたのは exe ファイルです。 参照するファイルがなくなっていれば多分エラーになるでしょう。 戻せば回復するでしょう。多分きっと。 ※ 元に戻せるようきちんと確認してから行ってください
ApplicationException: Unable to find csharp compiler in /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/lib/mono/4.5 UnityEditor.Scripting.Compilers.MonoCSharpCompiler.GetCompilerPath (System.Collections.Generic.List`1[T] arguments) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs:88) UnityEditor.Scripting.Compilers.MonoCSharpCompiler.StartCompiler () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs:59) UnityEditor.Scripting.Compilers.ScriptCompilerBase.BeginCompiling () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs:43) UnityEditor.Scripting.ScriptCompilation.CompilationTask.QueuePendingAssemblies () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:191) UnityEditor.Scripting.ScriptCompilation.CompilationTask.Poll () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:137) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.ScriptAssemblySettings scriptAssemblySettings, System.String tempBuildDirectory, UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.Scripting.ScriptCompilation.EditorBuildRules+TargetAssembly[]& notCompiledTargetAssemblies) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:749) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:627) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.TickCompilationPipeline (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:896) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface+<TickCompilationPipeline>c__AnonStorey3.<>m__0 () (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:219) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.EmitExceptionAsError[T] (System.Func`1[TResult] func, T returnValue) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:75) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface:TickCompilationPipeline(EditorScriptCompilationOptions, BuildTargetGroup, BuildTarget)
やはりエラーになりました。ということは、Unity が利用しているコンパイラはこの mcs
で間違いないでしょう。
細かい話をすると、バイナリファイル(bin/mcs)をリネームしてもエラーにはなりません。エラーになるのは実行ファイル(lib/.../mcs.exe)を対象にしたときです。
しかし mcs.exe をリネームすると、bin/mcs を直接実行した場合にもエラーが出ます。
$ /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/mcs --version Cannot open assembly '/Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/../lib/mono/4.5/mcs.exe': No such file or directory.
この挙動から、bin/mcs が内部で mcs.exe を利用していると判断できます。
そのため、DLL 化する際にはこの mcs
を使うことで Unity 開発時と同じ環境のコンパイルができるはずです。
通常の mcs
と Unity 内部の mcs
を比較すると、次のような差が確認できます。
$ mcs --version Mono C# compiler version 6.0.0.0 $ /Applications/Unity/Hub/Editor/2017.4.25f1/Unity.app/Contents/MonoBleedingEdge/bin/mcs --version Mono C# compiler version 5.0.1.0
ドキュメントにあった .NET の差分から推察すると、新しい方の mcs
では .NET Core の実装を参照していると考えられます。
(この点については未検証です)
解決
Mcs := $(UnityDirectory)/Contents/MonoBleedingEdge/bin/mcs make_dll: $(Mcs) \ -r:$(UnityDirectory)/Contents/Managed/UnityEngine.dll \ -target:library \ -recurse:'Assets/Scripts/*.cs' \ -out:AROW.dll
先ほど述べたとおり、Unity 内部の mcs
を利用することで今回の問題は解決できました。
開発環境の精査を怠ったことが原因となったトラブルでしたが、コンパイラについて調査するいい機会となりました。
余談
Unity 2018 の場合
Unity 2018.4 で試したところ、先ほどの mcs.exe をリネームしてもエラーが起きませんでした。 おそらく原因は、こちらにあるように Unity 2018.3 以降の .NET 4.x 系統では Roslyn が使われていることかと思われます。
Roslyn は csc
を使っています。
そのため、次の csc(.exe) をリネームすると同様にエラーが発生します。
// Mac /Applications/Unity/Hub/Editor/2018.4.0f1/Unity.app/Contents/Tools/Roslyn/csc // Windows C:\Program Files\Unity\Hub\Editor\2018.4.2f1\Editor\Data\Tools\Roslyn\csc.exe
Exception: 'C:\Program Files\Unity\Hub\Editor\2018.4.2f1\Editor\Data\Tools\Roslyn\csc.exe' not found. Is your Unity installation corrupted? UnityEditor.Scripting.Compilers.MicrosoftCSharpCompiler.ThrowCompilerNotFoundException (System.String path) (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs:65) UnityEditor.Scripting.Compilers.MicrosoftCSharpCompiler.StartCompilerImpl (System.Collections.Generic.List`1[T] arguments, System.String argsPrefix) (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs:112) UnityEditor.Scripting.Compilers.MicrosoftCSharpCompiler.StartCompiler () (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs:220) UnityEditor.Scripting.Compilers.ScriptCompilerBase.BeginCompiling () (at C:/buildslave/unity/build/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs:60) UnityEditor.Scripting.ScriptCompilation.CompilationTask.QueuePendingAssemblies () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:228) UnityEditor.Scripting.ScriptCompilation.CompilationTask.Poll () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/CompilationTask.cs:135) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScriptAssemblies (UnityEditor.Scripting.ScriptCompilation.ScriptAssembly[] scriptAssemblies, UnityEditor.Scripting.ScriptCompilation.ScriptAssemblySettings scriptAssemblySettings, System.String tempBuildDirectory, UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.Scripting.ScriptCompilation.CompilationTaskOptions compilationTaskOptions, UnityEditor.Scripting.ScriptCompilation.EditorCompilation+CompileScriptAssembliesOptions compileScriptAssembliesOptions) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:1149) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.ScriptAssemblySettings scriptAssemblySettings, System.String tempBuildDirectory, UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.Scripting.ScriptCompilation.EditorBuildRules+TargetAssembly[]& notCompiledTargetAssemblies, System.String[]& notCompiledScripts) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:1047) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.CompileScripts (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:939) UnityEditor.Scripting.ScriptCompilation.EditorCompilation.TickCompilationPipeline (UnityEditor.Scripting.ScriptCompilation.EditorScriptCompilationOptions options, UnityEditor.BuildTargetGroup platformGroup, UnityEditor.BuildTarget platform) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:1377) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface+<TickCompilationPipeline>c__AnonStorey5.<>m__0 () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:331) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.EmitExceptionAsError[T] (System.Func`1[TResult] func, T returnValue) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:97) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface:TickCompilationPipeline(EditorScriptCompilationOptions, BuildTargetGroup, BuildTarget)
Visual Studio の場合
Visual Studio で DLL を作成した場合は今回の問題は起きませんでした。 実際に実行されているコマンドは次のものです。
/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/msbuild/Current/bin/Roslyn/csc.exe /noconfig /nowarn:1701,1702,2008 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG /errorendlocation /preferreduilang:ja-JP /highentropyva+ /reference:/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/4.7-api/mscorlib.dll /reference:/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/4.7-api/System.Core.dll /reference:/Library/Frameworks/Mono.framework/Versions/5.18.1/lib/mono/4.7-api/System.dll /debug+ /debug:portable /optimize- /out:obj/Debug/Temp.dll /subsystemversion:6.00 /target:library /utf8output MyClass.cs Properties/AssemblyInfo.cs "/var/folders/j2/xntwf0xj7311rg06_2p570d00000gp/T/.NETFramework,Version=v4.7.AssemblyAttributes.cs"
試したところ、mscorlib
を指定することで本現象が解決できるようです。
ただし mcs
で試した場合は System
などで多重定義のエラーが起きるため、noconfig
などのオプションが必要になります。
csc
に乗り換えるリスクや、実行コマンドが複雑になることを避けるため、今回の問題解決としてはこれらの手段は採用しませんでした。
GitHub の Wiki で子ページを一覧化する
※ やりたいことはまだ途中なんですが、キリがよくなったのでいったんまとめています
やりたいこと
GitHub で Wiki を書くと、サイドバーに積み上がっていきます。 これは増えすぎると隠れてしまうので、一覧で表示されればいいなと思います。
やったこと
Wiki はそれ自体もリポジトリを clone することが可能です。 Wiki ページの右下で URL を確認できます。
今回の場合だとこんな感じです。
https://github.com/ryuj/Unity.wiki.git
確認したところ、生成したページが .md ファイルとして存在します。 トップページは Home.md です。 なので、以下のように対応を考えます。
- Home.md 以外の .md ファイルをリンクのフォーマットで出力する
- 出力結果をリストにして Home.md へ上書きする
上記に対応したスクリプトは以下です。 これを実行すれば、Home.md に子ページの一覧を作成できます。 (Ruby は思いつくまま書ける感じが好きですが、Ruby ぽく書けているのかは未知数)
HOME_FILE = "Home.md" def main html = "" Dir.glob("*.md").each do |file| next if file == HOME_FILE base = File.basename(file, ".md") s = "* [#{base}](./#{base})\n" html << s end File.open(HOME_FILE, "w") { |io| io.puts html } end main
できていないこと
現状、スクリプトを手動で実行しなければ一覧化できません。 Wiki はブラウザ等で編集することが多いと思います。 なので、ブラウザ等で編集された際に上記スクリプトを動かして Home.md を更新する、というのがベストです。
git log で確認したところ、ブラウザからの編集もコミットログで残っているため、おそらく普通に push を検知できれば行けそうです。 未確認ですが、このあたりはおそらく webhook で対応できるのでは、と予想してます。(あとでやる、つもり、、、)
「Unity Designer's Cafe #2」に参加しました
本日は、こちらの勉強会に参加しました。
unity-designers-cafe.connpass.com
しばらく勉強会は行ってなかったので、よい刺激になりました。(当日朝まで補欠だったんで不参加のつもりでしたが)
本日の目的
本イベントはデザイナーの方が中心のイベントです。自分はエンジニアですが、以下の目的で参加しました。
- シェーダーなどの見た目系 Tips 聞きたい
- デザイナー観点での困ったことや解決事例を聞きたい
一つ目はまぁ誰しも聞きたい内容だと思うのですが、今回は特に二つ目に興味がありました。自分は以前のチームでもデザイナー向けのツールを作った経験があり、以下の実感がありました。
- エンジニアの力で他の職掌の課題解決ができること
- ただしそのためにはきちんとワークフローを理解するのが重要なこと
- 課題には本人たちも気づいていないようなものも含まれること
そのため、どんな課題があるか、またそのあぶり出しにどんな取り組みが行われているのか、という点に興味がありました。
本日の感想
上記の目的から言うと、@LandyNagata さんの「プログラマに相談したらデザイナーの仕事が楽になった話」が一番参考になりました。(もちろん他の発表も非常に面白かったです)
ここでは、実際にエンジニアが作った事例が紹介されていました。
- ImportSettings の自動設定
- RGBA16bit ディザ加工
- スプライト指定で使用している prefab リストアップ
- オブジェクト透過キャプチャ
- アセットバンドルとマスターが絡むオブジェクトのビューア
特に5番のビューアは、プランナーのデータ設定時やエンジニアのデバッグ時にも役立ちそうだなと感じました。また、ツールで解決すべき単調な作業を「お刺身たんぽぽ」と表していたのも印象的です。(エンジニア観点で言えば、名前をつけるのは課題解決への第一歩だと思います)
懇親会でもそのあたりのお話をさせていただき、各社似たような課題を感じているのは実感できました。改善の取り組みとしては、以下のようなものが挙がっていました。
- 専用チャットなどやり取りしやすい場を用意する
- 席を近くして日常的に相談しやすくする
- 困ったらあの人に聞こう、というような人をたてる
- 振り返り会で積極的に課題を挙げ、みんなで解決を意識する
弊社でも似たような取り組みを行っているため、少なくとも間違ってはいなそうだという実感が得られたことや、自分たちの取り組みから得られた知見もぜひ発表してみたいなと感じられたのは、非常に良い収穫だったと思います。