Galapagos Tech Blog

株式会社ガラパゴスのメンバーによる技術ブログです。

PM始めて半年くらい経ったので振り返る

AIR Design for Marketing 事業部プロダクトマネージャー(以下、PMと表記します)の安永です。エンジニアなのですが今年からPMもやっています。

PMってやること多いですよね。巷ではミニCEOと言われることもあります。実際は組織やプロダクトの状態によってPMの定義は様々だと思いますが、未定義の場合は「プロダクトのために何でもやる人」という扱いを受けるのではないでしょうか。

当事業部でもPMという役割ができたばかりでまだ定義が曖昧なのですが、何でもできちゃうPMがいることもあってかPMは何でもやる雰囲気があります。

勿論何でもできることに越したことはないのですが、駆け出しPMの自分にとっては正直厳しく、タスクに溺れて本来PMがやるべき「プロダクトの方向性を決める」ための仕事ができなくなっていました。数ヶ月前の話です。

それからいろいろ試行錯誤を経て、今は少しは息継ぎできるくらいにはなってきました。

この記事では当時陥っていた問題と、試行錯誤の一部を紹介し、今どのように改善されたかを振り返ってみたいと思います。

プロダクトの方向性が決まらないうちに走り出すプロジェクト

私が担当することになったのは、社内の特定業務(といっても成熟した固定的なプロセスではなく、多様で変化し続けているプロセス)における課題を解決するためのプロダクトでした。

既にビジョンはあったものの、ビジョン実現のためにどんな課題を解決すればいいのか、どんな機能を作っていけばいいのかを示すプロダクトの方向性は決まっていない状態でした。

今思えばここでプロダクトの方向性を一時的にでも固めておくべきだったのですが、既にざっくりと機能の要求があったことと、「エンジニアのリソースを余らせるのはダメ」という指示があったこと、そして私のPM理解が浅かったためSTOPをかけれず、開発が始まりました。

(ちなみにこのときの機能の要求とは、「とあるツールで情報収集をしているが大変なので効率化してほしい」という要求でした。今なら「なぜそのツールで情報収集をしているのか?」「そのプロセスはどのくらいの頻度で発生するのか?」など開発の前に明らかにしなければならないことが思い浮かびますが、当時はそのまま受け入れてしまいました。)

タスクに溺れるPMと将来の見えないプロダクト

当時PMとしてやっていた仕事は、

  • 要求をヒアリングして具体化、整理
  • 機能の要求定義、仕様策定
  • エンジニアのタスクを洗い出し、アサイン、進捗管理、レビュー
  • 事業部マネージャーやターゲットユーザーが属するチームのマネージャーへの進捗報告、その資料作成
  • ユーザーサポート、ユーザーインタビュー

という感じでした。(この他にエンジニアとしての業務が発生することもありました。)

開発はスクラムでやっていましたが、その状況は、

  • チーム構成はPMとエンジニア
  • スクラムマスターとプロダクトオーナーをPMが担当
  • スプリントは1週間単位
  • レトロスペクティブ(チームに対する振り返り)はなし

という感じでした。

結果、エンジニアに渡すタスクの準備が間に合わないという形で破綻しました。スプリントプランニングまでにタスクを準備できず、プランニング内でタスクについて議論することになり、プランニングが終わらない、という事態が発生するようになりました。

見積もりもできる状態ではなく「いつリリース?」に回答できません。(そもそもこの段階で機能リリースのスケジュールに注目されている時点で期待値調整に失敗しています。)

プロダクトの方向性を考える時間も取れないので「次何するの?」に回答できず、ステークホルダーの御用聞き状態。ビジョンに近づいてるのかわかりません。

いずれの仕事も「それがPMの仕事でしょ」という扱いだったので助けてもらうこともできません。自分が駆け出しPMだから、力不足だから、と塞ぎ込み。とはいえPMとして相談してもらいやすいように明るく振る舞う。

この頃はなかなかつらみが深く、心が折れかけていました。。

チームで機能を考えてわかった要求の曖昧さ

タスクの準備が間に合わない、となってまずはエンジニアのテックリードに相談しました。決まった仕様を実装するために必要なタスクを洗い出してもらい、一時的になんとかなりましたが、長くは続きませんでした。

要求を満たすためにどんな機能、どんな仕様にすればいいかをPM1人で考えていたので、結局そこがボトルネックになっていました。同時に、なぜ1人で考えているんだろうという違和感がありました。自分がエンジニアだった頃、問題解決するためにどんな機能にするかはエンジニアが考えていました。エンジニアはどのように問題解決するかまで含めて考える力があると思っています。そして今、自分が率いている開発チームにはエンジニアがいます。チームで考えた方が絶対いいアイデアが出るぞ、と。

そこで次の一手として、タスクの準備を開発チームでやってみることにしました。

具体的には、開発チーム全員で集まり、MindMeisterというマインドマップツールを用いてタスクを分解していく会議体を設定しました。

目的としては、

  • 機能を実装するのに必要なタスク(チケット)をチームで作る。その過程でどんな機能・仕様にするかもチームで議論する。
  • マインドマップで機能やタスクの関係を可視化&なるべく細かくタスクを分解し、タスク内容を具体化する。これによりチームでの認識を揃え、タスクを受け取りやすく、見積もりをしやすくする。

を期待していました。

マインドマップによるタスク分解のイメージ。

これはうまくいきませんでした。

それまではPM1人で機能を考えていたので気付きませんでしたが、要求定義に曖昧さがあったために、機能アイデアが発散してまとまりませんでした。プランニングで議論が終わらないという問題が、前倒しになっただけでした。

また、機能・仕様が決まらないと設計はできず、設計しないと実装タスクの洗い出しはできないので、結局ほとんどタスクの洗い出しは進みませんでした。

しかしながらわかったこともありました。

  • 要求定義が曖昧だったということ
  • 要求定義が具体的になれば、エンジニアが機能を考えることはできるということ
  • 要求定義を具体化するための材料(ヒアリングや検証)が足りていないということ

です。

チームで意思決定して開発プロセスを改善していく

ここからさらに試行錯誤を重ね、さらに新メンバー(スクラムマスター資格持ち!)が加入したことによって、状況はだいぶよくなりました。この新メンバーはスクラムマスターとして加入したわけではありませんが、彼のアドバイスは試行錯誤に大きく影響を与えており、とても助けられました!

まず、PMは要求定義に責任を持ち、その要求を満たすために何をすべきかはエンジニアが責任を持つ、というルールをチームで作りました。これによりタスクの洗い出しやアサインをエンジニアに委譲することができました。要求定義は「誰の、何のための機能か」「どんな機能か」「受け入れ条件」とフォーマットに沿って書くようにし、チームにレビューしてもらい合意が取れてから着手するようになりました。機能アイデアのベースをPMが作っているところは変わっていませんが、「何のための機能か」「受け入れ条件」を明確にすることでエンジニアからフィードバックをもらいやすくなり、合意までがスムースになったと思います。

これ以外にも、チームのポリシーをドキュメントにするようにしました。タスクに影響するようなポリシーから、細かいところでSlackでメンションするときのポリシーまで、チームで議論になって方針が決まったものはポリシーとしてドキュメントにしました。あくまでポリシーなのでそれに縛られるものではなく、日々の仕事や議論の中で判断に迷ったときに参照するものとして使っています。これもタスクに関するチームの合意をスムースにしています。また、エンジニア側でも開発ルールなどをドキュメント化するようになり、チームの意思決定を形にして残す文化が醸成されつつあります。「形にして残す」ことは、議論や別のドキュメントを作る際に参照することができ、また、誰に聞かずともいつでも参照できるという点で重要だと思っています。

また、以前は基本的に実装に関するタスクだけを扱っていましたが、技術的な検証や、機能アイデアに本当に価値があるかを確かめるPoC的な検証もタスクとしてエンジニアにお願いできるようになりました。これによって実装する前に判断材料が増え、要求定義や優先度を見直せるようになりました。チームから意見できる材料にもなり、単なる御用聞きから一歩抜け出せたのではないかと思います。

地味に大きかったのがスプリント単位を1週間→2週間にしたことです。同時に進捗報告も2週間ごとにしたことで、会議やスクラムイベントの準備にかかっていた時間が大幅に削減されました。

レトロスペクティブ(チームに対する振り返り)も実施されるようになり、以降継続的に開発プロセスの改善がされるようになりました。以前は何か改善の取り組みを相談するのも心理的なハードルがありましたが、明示的に相談できる場が設けられたことによって、相談しやすくなったと思います。

まとめ:PMが今やるべきことを見極める

ここまで述べてきたような経験を経て、1人でできることの限界と、チームのありがたみを改めて感じました。

プロダクトのために何でもやるべきPMですが、現実問題として何でもはできません。なので、プロダクトのために限られたリソースで何をするかが重要になります。プロダクト開発の施策として「何をやるか」「どの順番でやるか」を考えるのは当たり前ですが、これは個人のタスクについても意識しておきたいです。

そして「今やるべきこと」が1人で難しいのであれば、チームに協力してもらうためにやるべきことが「今やるべきこと」になるでしょう。いずれにしても、自分が今やるべきことを見極めて実行していくことが、プロダクトのためでもあり、チームへの感謝・還元することになると信じて、これからもやっていきたいと思います。

www.wantedly.com

「TCAつかってみます」に参加します

AIR Design for Apps 事業部 アプリエンジニアチーム iOSユニットの亀澤です。
試用期間を終えて正式に社員になりました。

さて、突然ですが皆さんはiOSのプロジェクトでアーキテクチャを採用していますか? MVCとかMVVMとかVIPERとか、聞いたことあるけど本格的に始めるというほどではなかったのですが、今回 the Composable Architecture(以降TCA )を採用したプロジェクトに参加することとなりました。 転職前は自社サービス、それも古くからサービスを行っているアプリケーションが多かったので、アーキテクチャをやっと取り入れようとしていたり、SwiftUIも使う機会がなかなかありませんでした。以下今回TCA+SwiftUIを採用したプロジェクトに初参加した感想です。

TCA採用の経緯はこちら

techblog.glpgs.com

さらっと解説を見た感じでは

  • SwiftUIとの親和性が高い
    今回はSwiftUIですがUIKitもサポート
  • テストが書きやすい
    画面や外部に依存する部分を予め考えて作るのでロジック部分のテストが書きやすい アクションを送ったらちゃんとstateが変わるかの確認ができればviewと分離されているのでロジックの担保がしやすい

ふむふむ、今どきな感じですね。あとはやってみて慣れるしかないかなと飛び込んでみました。

始めてみるとやっぱり読んだだけでは理解できてないですね、色々とハマりました。

最初に作ったのはこんな感じ

AppがあってHomeが表示されていてそこからモーダル画面の中にある詳細表示のViewにあるパーツのView(赤いView)を追加します。 緑がアプリケーション、灰色の枠はViewControllerでその他がViewと思って下さい。 それぞれの枠にStore(State,Action,Reducer)があります。

  • ついViewやStateに書いてしまう
    表示したViewサイズの画像をAPI経由で取得したときに、表示されたタイミングでそのまま受け取った情報を反映してしまったり。 Stateを初期化の時に色々加工してしまって後々テストデータを注入できなかったり 初めて作ったときだったのでStoreを作るほどでもないかとViewだけで作ったのですが、ここだけTCAに対応しないViewにしてしました。 地図を表示するためにAPIを使ったのですが、OnApprearでViewにOnAppaerでAPIからURLから画像を更新しています。 URLが状態ですのでStateに入れてonAppearでアクションを送りreducerでAPIを使いURLを更新して画像を読み込むのが正解ですね。

今度はちゃんとView・State・Reducerを持つViewのパーツを作りました。

struct PartsStore {
    static let reducerCore = Reducer<State, Action, Environment> { state, action, environment in
        switch action {
        case .tapButton:
            return .none
        }
    }
}

enum PartsAction: Equatable {
    case tapButton
}

struct PartsView: View {
    var body: some View {
        Button(action: { partsStore.send(.tapButton) },
                     label: { Text("button"} )
    }
}
  • どこのreducerで処理したらいいのか迷う
    最初に作ったのはモーダル画面に貼られる地図表示のViewなのですが、Actionをどこで拾うかで迷いました。
    作ったViewのStateとReducerの他に、貼り付けられているViewのReducerとアプリケーション元のReducerが出てきます。
struct TopState {
    static let reducerCore = Reducer<State, Action, Environment> { state, action, environment in
        switch action {
        case  PartsAction(.tapButton): 
            return .none
        }
    }
}

struct ParentState {
    static let reducerCore = Reducer<State, Action, Environment> { state, action, environment in
        switch action {
        case PartsAction(.tapButton):
                return .none
        }
    }
}

受け取ったActionを自分のreducerから上のreducerにActionを変えて送るのが正しいのか、そもそも受け取るのが自身のReducerなのか、親のReducerなのか、さらにその元となっているReducerなのかとか混乱しました。画面遷移をするのでパーツである自分の Actionをリレーしていく仕組みが理解できるまで苦労しました。

  • イベントが反応しない Viewで発生したイベントをそのStore単体内で処理するのは良いのですが子画面、孫画面となった場合reducerもツリーにしてイベントを渡していかないとアクションが届かないし、作った画面内では動いていたのにいざ画面を連携させてみたらイベント飛んでこないとか 親画面にアクションを繋げたくて親画面で作ったActionを使ってしまったりとイベントチェーンのつながりが慣れるまで辛かったですね。 上のコードで省略していますが、親が子へアクションがつながるように子を作る際に作ります。作り忘れるとアクションが届かないので上から順番に届いているか見てみると途中で途切れていたりします。
  • テストがしにくい
    経験不足からいまいちな実装してしまいテストできるようにするのに困りました。 正確にはテストが「しにくい」じゃなくて「しにくくしていた」のは自分だったのですが、ちゃんとDIすべき所はするようにしないとダメですね。

とハマったところばかりで悪い感じに書きましたがいいところもあります。

  • テストしやすい
    先ほどと矛盾していますがちゃんと書いていれば enviromentでDIする環境が整っているので、アクションを送信してStateがどう変わったかを見れば良いし、 画面はSwiftUIで作っているので既に用意してあるテストデータをツッコめば確認できます。 あるアクションを送ってその処理でさらに次のActionを送っている場合、次に受信しているActionを拾っていないと警告出るので取りこぼしもないです。
  • 書きにくいときは書くところを間違えている
    なんとなく何かの処理を書こうとして詰まる、必要なデータにアクセスできなかったり、そんな時には書く場所を間違えています。 一つずつきちんと書ければ難しいことはないのですが、つい余計なことをしようとすると急にわけがわからなくなります。 あれ?!と思ったら適切にしてなかったと気付けます。

なかなか慣れずに苦労しましたが、慣れてくるとなかなか楽しいです。 ためしにTCAを触ってみるのもいかがでしょうか?

Serverless Stack (SST) - サーバーレスフレームワーク for AWS

AIR Design for Marketing 事業部 バックエンドエンジニアの成田です。CloudFormation だいしゅきです。

サーバーレスフレームワークである Serverless Stack (SST) の v1 リリースを記念した YouTube 上のイベント SST 1.0 Conf が開催されていたようので、実際に触ってみました。

続きを読む