複数チームに分かれたプロダクト開発スタイルをかえって不自由に感じてはいないだろうか。チーム間に張り巡らされた無数のチェーンが自由を奪い、チームの活動を束縛する。そんな感覚だ。
組織を複数のプロダクト開発チームに分割する組織アーキテクチャは、マイクロサービスアーキテクチャに例えることができる。そこから見いだせる原則は、チームをコンポーネントとして捉え、凝集度を高く、結合度を低く設計することだ。この原則を軽視すると、チーム間の依存関係が互いをチェーンのように繋ぎ、絡み合い、組織全体を「分散されたモノリス」に変えてしまう。その結果として、チームは日々、多大な調整コストや遅延コストを支払い続ける羽目になる。
では、既存のソフトウェアプロダクト開発において、個々のチームが活動しやすい分散型組織の設計とはどういうものだろうか。あくまでも私の経験や考えに基づくものではあるが、本稿はこれをテーマに書いてみたい。
「分散されたモノリス」と化したプロダクト開発組織
チーム間を縛りつける制約というチェーン
「凝集度(cohesion)」と「結合度(coupling)」は、ソフトウェアコンポーネントのモジュール性(modularity)の程度を示す尺度だ。 凝集度が高いほど、また、結合度が低いほど良いとされる。
凝集度が高いコンポーネントは、それが提供しようとする特定の機能に対し、関係性の高い責務を網羅的に含み、関係性の低い責務をほとんど(あるいは全く)含まない。凝集度が高いと結合度が低く、凝集度が低いと結合度が高い傾向がある。
チームとは、組織を構成するコンポーネントだ。ソフトウェアコンポーネントと同様に、チームにも凝集度と結合度がある。そして、チーム間の結合は、チームの活動に制約を課す。
本来、デリバリパフォーマンス上で好ましい組織の状態とは、チーム間が疎結合であり、それぞれのチームが独立してミッション遂行が可能であることだろう。チームが何かをしようとするたびに、他のチームに何らかの協力を依頼せざるを得ないようでは、高いパフォーマンスは期待できない。
しかし、結合による制約は、チーム間をチェーンで繋ぐかのように、互いの活動に干渉する。これらがクリティカルチェーンやクリティカルパスを形成し、ソフトウェアデリバリのリードタイム(lead time for changes)を悪化させる。それはまるで、互いが無数のチェーンで繋がれた「分散されたモノリス(distributed monolith)」のようだ。
本来、「分散されたモノリス」という言葉は、サービス間が密結合になってしまったマイクロサービスアーキテクチャを指すアンチパターンの名だ。 しかしながら、組織設計のアンチパターンとしても、何ともしっくりくるではないか。
制約を生じさせる代表的な三つの結合要因
制約というチェーンを全て完全に断ち切ることは、おそらくできないだろう。しかし、その数を削減したり、緩和することはできる。そのためには、分散組織の凝集度を可能な限り高く、結合度を可能な限り低くなるよう、デザインする必要がある。
自組織の凝集度・結合度のレベルは、二つ以上のチームが参加するミーティングの実施頻度で把握できる。そこで話し合われているのは、互いのチーム活動に干渉し合う制約に対する調整だ。このような調整が頻繁に行われる組織は、チーム間の結合度が高い。
その調整のテーマは概ね次の三つのいずれかに大別できるのではないか。
- 複数チームによる人員の共有
- 複数チームによる同一案件の実施
- 複数チームによる同一コンポーネントへの変更
ソフトウェア開発の現場でよく見られるこれらの行為が、なぜチーム間の結合要因となるのだろうか。そこで生じる制約とはどういうものなのだろうか。
「複数チームによる人員の共有」による制約
チーム間での人員の共有、すなわち「兼務」や「兼任」と呼ばれるそれは、チーム間の結合度を高める要因となる。人的リソースの利用を接点として、チームとチームが互いのスケジュールに干渉し合うからだ。
二つ以上のチームが、ひとりの人員に対し、同時期に作業をスケジューリングしようとすると競合が起きる。その解決方法は二つ。ひとつは優先順位を決めて順に作業を終わらせること。もうひとつは複数の作業を同時に進めること。
前者の直列方式では、優先順位が低いとされた作業を抱えるチームが、先行の作業が終わるまで待たなければならなくなる。後者の並列方式では、マルチタスキングによって、いずれのチームの作業も、実行期間が長くなる。どちらにしても、結局はリードタイムを悪化させることになる。
兼務の問題は以前の記事「兼務はチームの独立性とのトレードオフ」で詳しく書いたので、そちらも参照してほしい。
「複数チームによる同一案件の実施」による制約
機能追加や機能改善といった開発案件をチームが進める上で、必要となる責務をチームが網羅できていない場合、欠けた責務を担う他のチームに協力を仰ぐことになる。つまり、チーム機能の凝集度が低いのだ。
ひとつの開発案件を複数のチームで協力して進めるには、互いのスケジュールを調整する必要がある。しかし、協力を依頼されたチームも手が空いているわけではなく、常に様々な開発案件を抱えている。そのため、依頼先チームでの作業スケジューリングが、依頼元チームの期待通りになるとは限らない。結果、このような協力関係は多くの場合、リードタイムを悪化させることになる。
この問題は、「バリューストリーム」という観点で別の記事「マルチバリューストリームというアンチパターン」でも取り上げたので、そちらも参照してほしい。
「複数チームによる同一コンポーネントへの変更」による制約
二つ以上のチームがそれぞれの開発案件の都合で、同時期に、同じコンポーネントに対して変更を加えようとすることがある。これが、チーム間の結合度を上げる。そうすると、変更しようとするコンポーネントに関するテストやデプロイの実施日について、チーム間での調整が必要となる。
テストやデプロイの実施を、チームを跨いで統合実施しようとすると、その実施タイミングはもっとも遅いチームに合わせることになる。逆に、テストやデプロイの実施をチーム間で優先順位を決めて順に実施しようとしても、優先順位が低いチームは先行チームの実施完了を待つことになる。いずれにしても、リードタイムを悪化させてしまう。
結合要因の排除
以上のように、三ついずれの結合要因においても、それぞれの制約によって、ソフトウェアデリバリのリードタイムを悪化させるに至っている。この制約が、冒頭で述べた「チームの活動を束縛する」という感覚を生じさせているのだろう。
本稿は、この三つの結合要因を可能な限り排除し、制約を削減・緩和することを戦術として、以下に具体的な施策を講じている。
チーム分割と協働のデザイン
チームというアトミックで不可分な単位としての活動
チーム内で複数の開発案件が並列で走ることはあり得る。しかし、マネジメント主体がそれぞれの開発案件ごとに分かれているなら、それはもはやチームとしての体を成していない。
こういうケースでは往々にして、個々の開発案件ごとにプロジェクト体制が敷かれ、専任のプロジェクトマネージャーが立ち、企画担当も含め、いくつかのチーム・組織から人が集められる。チームの存在は、都度発生するプロジェクトに人的リソースを提供するリソースプールでしかない。
このような体制に関する問題は、note に書いた記事「ソフトウェアプロダクトの機能追加・機能改善をプロジェクト体制で進めるのはもうやめよう」でも扱った。
そこでも書いた通り、プロジェクト体制は、兼務体制を生みやすい。 つまり、「複数チーム(プロジェクト)による人員の共有」による制約を受けやすくなる。
また、「複数チーム(プロジェクト)による同一コンポーネントへの変更」による制約も受けることになる。
だからこそ、プロダクト開発を担うチームは、アトミックでありたい。チームメンバーが個々に活動するのではなく、チームとして活動するということだ。チームは不可分な存在であり、チームとして開発案件の実行に責任を持つ。複数の開発案件が並走したとしても、チームとしてそれらをまとめてマネジメントする。これが、分散組織をデザインする上での核となる基本原則だ。
単一の担当チームによるコンポーネントの開発・保守・運用
ソフトウェアプロダクトは、複数のコンポーネントの集合体として存在するものだ。ここでは、このコンポーネントというものの粒度を、「何らかのプラットフォーム(動作環境)にデプロイする単位」としよう。この定義は、『Microservices』での component の定義とも一致する。
Our definition is that a component is a unit of software that is independently replaceable and upgradeable.
(中略)
One main reason for using services as components (rather than libraries) is that services are independently deployable.
開発組織を複数チームに分割する時は、この独立してデプロイ可能(independently deployable)なコンポーネントに着目することが鍵となる。
プロダクト開発組織を複数のチームに分割する主な理由のひとつは、それぞれのコンポーネントごとに、唯一の担当チームを割り当てることにあると言っても良い。Products not Projects にもあるように、コンポーネントの開発・保守・運用は、原則として担当チームだけで行う。
これは、Amazon.com の CTO のワーナー・ヴォゲルズの言う “You build it, you run it” にも通じる。
もし、ひとつのコンポーネントに対し、複数のチームが変更を加えようとすると、「複数チームによる同一コンポーネントへの変更」による制約を受けることになる。そしてそれは、調整コストや遅延コストとして跳ね返る。コンポーネントの開発・保守・運用を唯一の担当チームで行うことが原則である理由のひとつは、ここにある。
コンテキストと更新頻度に着目したコンポーネントの兼務
コンポーネントの数が、現実的に編成可能なチームの数より多いこともある。必然的に、ひとつのチームが複数のコンポーネントを担当することになる。
担当コンポーネントの組み合わせは、「境界付けられたコンテキスト(bounded context)」が同じもの同士を寄せるべきだろう。そうしなければ、チームの凝集度が下がり、「複数チームによる同一案件の実施」による制約を受けるようになるからだ。
また、それと同時にコンポーネントの更新頻度にも着目しなければならない。更新頻度が高いコンポーネントを複数抱えることになったチームは、人的リソースの枯渇が状態化し、開発案件が滞るようになる。そうなったチームは、他チームから一時的に人的リソースを借り受ける形で「複数チームによる人員の共有」を発動させることを繰り返すようになる。
このような事態を極力避けるためにも、更新頻度の高いコンポーネントと、低いコンポーネントを組み合わせるようにすることもポイントになる。チームの負荷を平準化することが狙いだ。
次の四象限のように、二つ目以降のコンポーネントの兼務は、主たるコンポーネントとコンテキストが同じかどうかと、更新頻度が低いかどうかで決定すると良いだろう。
数多くのコンポーネントの開発・保守・運用に要する負荷の軽減や平準化については、他にもいくつか方法がある。ただ、それらは本稿のテーマである組織設計とは外れるため、今回は割愛し、別の機会で取り上げたいと思っている。
依存関係に着目したコンポーネントの兼務
コンポーネント間に、強い依存関係が存在することも多い。そこに着目せずにコンポーネントの担当チームを分けてしまうと、コンポーネント間の依存関係が、そのままチーム間の依存関係になってしまう。つまり、「複数チームによる同一案件の実施」の発動だ。
このチェーンを完全に断ち切ることは難しい。コンポーネント同士の依存関係を弱めるには、日々の蒸留を続け、コンポーネントのコンテキスト境界を時間をかけて整理するしかないからだ。
もし、依存関係にあるコンポーネントの組み合わせが同一のコンテキストに属すものであるなら、同一のチームの担当とすると良いだろう。そうすれば、「複数チームによる同一案件の実施」の発動を回避できる。
一方で、依存関係にあっても、コンテキストが異なるコンポーネントであるなら、担当チームは分けておいた方が良い。このような関係にあるコンポーネントの担当をひとつのチームにまとめると、コンポーネント間の依存関係の結合度がより強くなる傾向があるからだ。
コントリビューター/コミッターモデルでのフィーチャー開発と協働
「複数チームによる同一案件の実施」の必要性は、やはりどうしても発生してしまうものだ。すべてのチームの凝集度を完全に高めきることは不可能だからだ。それに、たとえ凝集度を完全に高められたとしても、境界づけられたコンテキストを横断するフィーチャー開発というものも日常的に発生する。こういう時は、制約を受けつつも複数チームで協働する道を選ぶことになるだろう。
しかし、協働するにしても、チーム間でのスケジュールが上手く噛み合わず、どうしても他のチームの協力を得られそうにないケースもある。
また、協働で進めるためのオーバーヘッドが、作業規模に対して大きすぎることもある。チームが単独で進めた方が効率が良いケースだ。
いずれのケースでも、案件の主担当チームが、他チームが担当するコンポーネントに対して変更を加えることになる。そうすると、本来の担当チームによるコンポーネントへの変更と、他チームによるコンポーネントへの変更が同時期に発生する。こうして、「複数チームによる同一コンポーネントへの変更」が発動する。
この時の制約への緩和策としては、OSS 開発のような、コントリビューター/コミッターモデルでのチーム間協働が適している。ここで言うコントリビューターは、他チームが担当するコンポーネントを変更するチームのことで、コミッターは、そのコンポーネントを本来担当するチームのことを指す。
コントリビューターチームは、必要な変更をプルリクとしてコミッターチームに投げ、コミッターチームは、それをレビューし、マージした上で、任意のタイミングでデプロイする。
このやり方はあくまでも「複数チームによる同一コンポーネントへの変更」や「複数チームによる同一案件の実施」の亜種であり、制約は受ける。しかし、プルリクを介してプロデューサー/コンシューマーパターンが形成されるため、コントリビューターチームとコミッターチームがそれぞれ自分たちのペースで活動することが可能になる。つまり、調整コストが下がる上、コミッターチームの負荷もコントロールしやすくなる。
とは言え、やはり課題はいくつかある。最も大きい課題は、プルリクを投げたコントリビューターチームが期待するデプロイ日と、コミッターチームが実際にそれをデプロイする日を、どうやって合わせるかだ。
チーム間でデプロイ日の調整を行うこともできるが、できれば調整コストは下げたい。それを実現する決定的な方法を、私自身もまだ見出せていないが、ブランチ戦略によってある程度はカバーできる。
その一つの方法としては、あらかじめコミッターチームが、デプロイ日ごとにブランチを複数用意しておくことだ。コントリビューターチームは、その中から任意のブランチをマージ先として選んで、プルリクを投げれば良い。
もう一つの方法は、マージした変更が、速やかに本番環境にデプロイされることを、コミッターチームが保証するというものだ。これには、継続的デプロイ(continuous deployment, CD)によって、マージされた変更が自動でデプロイされるレベルのデリバリパイプラインが実現されていることが求められる。
いずれにしても、マージ後に手動テストが必要であればそれが制約となってしまう。CI/CD のパイプラインによって、デリバリまでのテストが自動化されていることが前提となるため、コントリビューター/コミッターによる協働は、実現難易度が高いと言えるだろう。
よくある問題へのソリューション
クライアントサイドとサーバサイドの垂直統合チーム
クライアントサイドとサーバサイドという切り方で、チームを分けている組織を見かける。ウェブ API をサーバサイドのサービスが提供し、それをクライアントサイドのアプリケーションが利用するという形で、コンポーネントが分かれているからだ。
しかし、両チームの活動をよく観察してみると、機能追加や機能改善といった開発案件を協力して進めていることが多く、「複数チームによる同一案件の実施」が頻発している。ひとつの開発案件に要するコンポーネントの変更が、クライアントサイドとサーバサイドの両方に及ぶからだ。
クライアントサイドとサーバサイドのコンポーネントは、このように強い依存関係にある。それは、コンポーネントを分割している理由が、システムアーキテクチャ上の都合でしかないからだ。基本的に、両者は同一のコンテキストに属している。これらのことを考慮せずに担当チームを分けてしまうと、チームの凝集度が下がり、それが「複数チームによる同一案件の実施」につながる。
この問題を回避する方法は、両方のコンポーネントを単一のチームで担当することだろう。つまり、クライアントサイドとサーバサイドを垂直統合したチームを作る。そうすれば、チームの凝集度を上げられる。それが意思決定を迅速にし、価値の提供に集中することを可能にする。
垂直統合型チームでは、メンバー個々のスキルセットの垂直統合も要求される。もしチーム内で、クライアントサイド開発のスキルセットしか持たないメンバー、サーバサイド開発のスキルセットしか持たないメンバーで分かれてしまうと、一方のスキルセットのメンバーが多忙な時に、もう一方のスキルセットのメンバーが助けに入れない。
スキルセットを垂直統合するといっても、各人がどちらのスキルも同レベルで高めるまでの必要はない。メンバー各自の志向やチームの状況にあわせ、専門とするスキルを深堀させつつ、他の領域のスキルに幅を広げる。いわゆる「T 字型人材」の集まりとしてチームが構成されていれば良い。
プラットフォーム別アプリケーションチームとプラットフォーム間移植
モバイルアプリを自社ソフトウェアプロダクトとして展開する組織は、iOS と Android、それぞれのプラットフォーム向けにアプリを開発していることが多い。このようなプラットフォーム別に存在するコンポーネントの担当を、「アプリチーム」といった形でひとつのチームに統合していないだろうか。
確かに、同一ドメインを扱うという点では悪くはないが、互いのコンポーネントの間には、クライアントサイド/サーバサイドのような依存関係はほとんどない。つまり、プラットフォームごとにチームが分かれていても、制約は生じにくいのだ。これは、デスクトップアプリの Mac 版と Windows 版や、ウェブアプリケーションの PC 版とスマホ版についても言える。
それならば、プラットフォームごとにチームを分けた方が、チームとしての小回りが効く。
もちろん、組織には人員数という制約条件があるので、無尽蔵にチームを作れる訳ではないが、可能な範囲で分けておくことをおすすめする。なぜなら、プラットフォームごとにチームを分けた体制は、互いに異なる機能を開発し、互いにそれらを移植するという、「たすきがけ」のような開発をやりやすくするからだ。同時に同じ機能を開発するより、いずれかのプラットフォームで作り上げた機能を、別のプラットフォームに移植する方が開発効率が良いはずだ。
それぞれのプラットフォームで同じ機能開発を進めることも可能だが、仮説検証を重視する昨今の開発スタイルに合わない。想定通りの価値を生み出せるかどうかわからない機能を、複数プラットフォームで同時に開発することは非効率だ。単一のプラットフォームで検証を繰り返し、機能を適度に磨き込んでから、別のプラットフォームに移植することで、無駄な開発を避けることもできる。
ただし、クリスマス向け機能といった、期限が重要な意味を持つ機能開発については、複数プラットフォーム同時で開発することになるだろう。
プラットフォーム別アプリケーションチームにおける垂直統合
ここで疑問がひとつ起きる。クライアントサイドのコンポーネントはプラットフォームごとに分かれているが、サーバサイドのコンポーネントは共有しているというケースでは、どのような分散組織をデザインすれば良いかだ。
このようなケースではまず、プラットフォームごとにクライアントサイドコンポーネントを担当するチームを分ける。その上で、サーバサイドのコンポーネントは、そのいずれかひとつのチームに垂直統合する。そして、サーバサイドのコンポーネントを担当しないチームは、必要に応じて、コントリビューター/コミッターモデルでサーバサイドコンポーネントに変更を加える。
負荷の高いチームへのコントリビュート型ヘルプ体制
あるチームが人手不足に陥った時、他のチームから、人手が貸し出されていないだろうか。このやり方は、「複数チームによる人員の共有」に該当してしまう。
とは言え、人手不足で困っているチームを放置するわけにはいかない。ここは考え方を変えてみよう。困っているチームに人を派遣するのではなく、困っているチームのタスクを切り出して、他のチームがそれを請け負うようにすれば良い。もちろん、請け負ったチームは、コントリビューターとして協働するのだ。
最後に - 組織は戦略に従い、システムは組織に従う
「うちの会社は組織を頻繁に変える」という愚痴を耳にすることがある。変化というのは受け入れられにくいものだが、そのような言葉が多く出てしまうようなら、組織を変化さる意図を、社員に上手く伝えられていないのだろう。
「組織は戦略に従う(アルフレッド・チャンドラー)」と言うように、組織を変える理由は、戦略を変えることにある。組織を頻繁に変えると言うのは、見方を変えれば、ビジネスを取り巻く内外の状況変化に対する組織の適応力が高いのだ。
プロダクト開発チームも組織の一部だ。ビジネス戦略の変化に応じて、プロダクト開発組織としての変化が求められる。
ではその戦略に従って、チームの分け方を柔軟に変えられるかというと、それは難しい。本稿で話してきた通り、既存のプロダクトのコンポーネントのあり方が、チームの分け方に強く影響を及ぼすからだ。戦略に合わせて無理な変化を加えてしまうと、そこで生じた制約によって、組織が「分散されたモノリス」になってしまう。
このことから、戦略に従って形作られたビジネス組織全体と、コンポーネントの構成に従って形作られたプロダクト開発組織は、ねじれが生じやすい。そしてこのねじれは、ビジネスパフォーマンスを阻害する要因になり得る。
ジレンマだ。ねじれが無いようプロダクト開発組織を編成すると、制約によってデリバリパフォーマンスを落とし、ねじれを受け入れるとビジネスパフォーマンスを落とす。
私はやはり、制約によるデリバリパフォーマンス悪化は避けるべきものだと考えている。制約を受け入れてしまうと、デリバリパフォーマンスの悪化がビジネスパフォーマンスを悪化させる。そのことは、書籍『LeanとDevOpsの科学』の調査からも明らかだろう。
制約を受けながらもビジネスパフォーマンスに影響を与えない方法があれば良いのだが、少なくとも現時点ではその妙案が、私には考えつかない。だから、デリバリパフォーマンスを最大化するプロダクト開発組織を設計することに注力する。その上で、そこで生じたねじれに対して、短期の対策と中長期の対策を講じる。
短期的には、戦略に従った組織との接点に、ねじれによるビジネスパフォーマンス低下を解消(あるいは緩和)する構造を設ける。簡単に言えば、戦略に基づくビジネス組織の動きを、プロダクト組織へ迅速に伝搬させ、連動させることを可能にする。これについてはまだ試行錯誤中なので、ここでは割愛する。
中長期の対策については、「組織は戦略に従い、システムは組織に従う」という状態を実現することだ。そうすればねじれも制約も避けることができる。
「システムは組織に従う」と言えば、「逆コンウェイ戦略(Inverse Conway Maneuver)」を思い出す。しかし、既存のソフトウェアシステムに対し、その構造に関係ない組織を組んでしまうと、先述の通り制約によってデリバリパフォーマンスが悪化しかねない。その上、システムアーキテクチャが組織アーキテクチャに追いついたころには、ビジネス戦略が変わって組織も変わってしまっている。それほどまでに、ビジネスを取り巻く外部環境の変化はめまぐるしい。
だから我々にできることは、個々のコンポーネントの責務を小さく、そして凝集度を高く、結合度を低くすることで、プロダクト開発組織の分割と、担当コンポーネントの編成を柔軟に組み替えやすくすることではないか。日々の地道な蒸留を続けていくことが、その実現の唯一の方法だろう。