mtx2s’s blog

エンジニアリングをエンジニアリングする。

9つのチームロールでチームワークを強化する / ベルビンチームロール

ティーブ・マコネル(Steve McConnell)の著書『More Effective Agile』の第6章で、「ベルビンのチームロール理論」なるものが紹介されている。そこに、「Plant」や「Shaper」「Resource Investigator」など、聞き慣れない9つのロール名が並ぶ。チーム内でこれらのロールのバランスが取れていることと、チームのパフォーマンスの間には、高い相関があるそうだ。

そう言われると興味を持つ。ベルビンチームロールとはどのようなものだろうか。しかし残念なことに、同書からはほとんど情報を得られない。書かれているのは、先の9つのロールそれぞれに関する短い説明文と、次の引用にある記述ぐらいだ。

この理論では、チームにおいて人々がどのように行動するか、人々がどれくらい協力して作業を行うと考えられるか、そして各役割の候補者をどのように選択するかを評価する。

2020年に日本語版が発売された同書を読んでから気になりつつも、調べることを後回しにしているうちにすっかり忘れてしまっていた。それが最近、「チームワーク」について色々と調べているうちに再びこのキーワードに出会い、思い出した。

ベルビンチームロール(Belbin Team Roles)は、チーム内での個人の行動特性をクラスタリングしたものだ。その9つのクラスターを、チームのパフォーマンスに効果的に貢献する「チームロール」として定義している。ソフトウェアプロダクトの開発チームで私たちが普段扱うロールと言えば、「スクラムマスター」や「テックリード」「アーキテクト」のように、チーム内での機能によるものだろう。ベルビンチームロールはそれとは違うようだ。

優れたチームワークを実現するには、チームメンバーそれぞれのソフトスキルやパーソナリティを活かした貢献も評価すべきだと、私は常々考えていた。それは実体験からだ。ソフトウェアプロダクトの開発と運用を小さな安定したチームで継続し続けていて気づいたことが、「チームが機能するかどうかは、メンバー個々の技術スキルの高さだけが決定要因ではない」ということだった。メンバーが互いを補い合っていると言えば良いのだろうか。それぞれの個性を活かしてチームに貢献していた。

ジェフ・サザーランド(Jeff Sutherland)の著書『スクラム』に、次のような一文がある。

チームが一つにまとまりシンクロし始めると、魔法にかかったようになる。チームがいる部屋に一歩入ればそれを感じる。チームが仕事に取り掛かると見えてくる。スムーズに流れるような動き。チームが一つになることで、個々の集まりを超えた境地に到達した状態だ。

私が体験したことはまさにこれだった。このような高いチームワークに再現性を持たせるためにベルビンチームロールは使えるかもしれない。本稿では、ベルビンチームロールを中心に、チームワークについて考えてみたい。

天才神話

チームワークについて考える前に、まず個人に焦点を当ててみる。

先述の書籍『スクラム』では、エール大学のプログラミング授業で出された課題に関する統計について記載がある。そのクラスを取っていたジョエル・スポルスキ(Joel Spolsky)による調査結果では、もっとも速く課題を仕上げた学生は、一番長く時間をかけた学生の10分の1の時間で終わらせていた。これは確か、ジョエル・スポルスキ自身の著書にも書かれていたように記憶している(現在、手元に本が無いので確認できないが)。

書籍『ピープルウエア』には、著者のトム・デマルコ(Tom DeMarco)らが1984年から1986年にかけて行ったプログラミングコンテストに関する記述がある。92社、のべ600人以上が参加したこのコンテストのデータを分析することで、プログラマの生産性にはバラツキがあることが明らかとなった。「コンパイル時のエラーを消し、デバッグに入れる状態までの所要時間」について、ある年のデータを見ると、最優秀者は平均より2.1倍速く、また中央値を挟んで上位半分は下位半分より1.9倍優れていた。他の年でも傾向は同様だったようだ。

トム・デマルコはまた、3つの文献(実際には4つのようだが)の調査結果を分析した結果として、次の3つの経験則を得ている。上記の調査結果は、これに見事に当てはまっている。

  • 最優秀者の測定値は、最低者の約10倍である。
  • 最優秀者の測定値は、平均的プログラマーの約2.5倍である。
  • 上位半分の平均測定値は、下位半分の平均の2倍以上である。

ソフトウェアエンジニアである私たちも、このような個人差は調査に基づかなくとも経験的に知っている。そのため、優れたソフトウェアシステムを生み出しているのは優れたソフトウェアエンジニアだ、と信じてしまいがちだ。書籍『Googleのソフトウェアエンジニアリング』では、これを「天才神話」と呼んでいる。

天才神話は、人間としての我々が、チームの成功を単独の人物/リーダーに帰せずずにはいられないという性向なのである。

身近に優秀なエンジニアがいれば分かると思う。「困難な障害が起きても迅速にシステムを復旧させ、根本的な原因を特定してしまう」「誰も解決できなかったシステムのパフォーマンス問題を粘り強く調査と実験を繰り返して解決してしまう」「皆に先行して設計と実装を進め、どのように開発すれば良いかチームを導く」「技術的な疑問に何だって答えてくれる」など。しかも、これは色んなタイプの天才ではなく、たった1人でこれだけの活躍をしてしまったりするのだ。そんなスーパーエンジニアがチームにいれば、そのたった1人がチームを成功に導いているような錯覚にチームメンバーは陥ってしまうのも仕方がない。

だが違う。同書の言葉を借りれば、「ソフトウェアエンジニアリングとはチームによる取り組み」なのだ。書籍『スクラム』によれば、約3,800のプロジェクトを分析した調査で、1番能力の高いチームが1週間でできた仕事を、1番能力の低いチームは2,000週間かかったと言う。つまりチームのパフォーマンスに2,000倍の開きがあったと言う。この結果はさすがに大きな外れ値に起因するのではないかと感じてしまうが、個人間でのパフォーマンス差の大きさより、チーム間でのパフォーマンス差の方がより大きく、そこにこそ注目すべきだとジェフ・サザーランドは言っているのだ。

※蛇足だが、ソフトウェアエンジニアリングにおける個人の能力は、単純にプログラミングの速さや欠陥の少なさだけでは計測できないと、私は考えている。これについては『Googleのソフトウェアエンジニアリング』でも言及されている「プログラミングとソフトウェアエンジニアリングの3つの違い」と同じ話である。例えばその1つである「時間」としては、「ソフトウェアを保守性のある状態に長期間保つ」という観点となる。書籍『Clean Architecture』でロバート・マーチン(Robert C. Martin)の言うところの「ソフトウェアをソフトに保つ」ということだ。プログラミングコンテストやプログラミング課題に関する調査結果では、この点は評価されていない。

アポロシンドローム

それでは、「天才的な人材ばかりを集めてチームを組めば成功が約束されるのだろうか」という疑問が湧く。ベルビンチームロールの生みの親であるメルディス・ベルビン(R. Merdith Belbin)は、まさにそれを実験していた。参加者が複数のチームに別れて経営手腕を競い合うゲームを実施するにあたり、そのうち1チームは、事前に行われた個人向けの知能テストで高得点であったメンバーのみで構成するようにしたのだ。そして、このチームを「アポロ」と命名した。

もちろん、アポロチームが好成績を収めることが予想されたわけだが、そうはならなかった。最下位となったのだ。その後、数年に渡り同様の実験が繰り返されたが、いずれのアポロチームも結果は散々だった。アポロとして編成された25のチームのうち、優勝したのは3チームのみで、8位以下がほとんどだった。この様子は、メルディス・ベルビンの著書『Management Teams』の2章に詳しく書かれている。

アポロチームの問題は、基本的にチームワークの悪さだ。本来、優秀であるはずの彼らの強みである競争心の強さや、クリティカルシンキングによる分析力・反証力の高さが裏目に出て、チームとしての協働を阻害してしまうのだ。これを「アポロシンドロームApollo syndrome)」と呼ぶ。

メルディス・ベルビンは、責任ある立場に知能面での優秀さが特に求められる産業領域として、コンピュータの応用分野と研究開発分野を挙げている。顧客企業にコンピュータシステムをインストールするチームを対象にした調査では、大半が高いクリティカルシンカーであることが分かった。特に、チームの中で最も賢く創造的なメンバーがプロジェクトマネージャーであった場合に、プロジェクトは最も失敗しやすくなるようだ。しかし、よりマネジメント能力に優れ、創造性に劣るメンバーがプロジェクトのリーダーを務めるようにしたところ、チームの技術力や想像力を損なわずに、有効性を高められた。

つまり、個人の優秀さのみにフォーカスした編成では、チームが上手く機能しないということだろう。

ベルビンチームロール

ソフトウェアプロダクト開発に関する書籍で、チーム編成に関する記述を読んでいて感じるのは、チーム内での役割が、プロセスに直接関連するような機能のみに焦点があてられて定義されていることだ。確かに、開発プロセスやプロジェクトを遂行するという観点ではそういった話題は必要だ。だが、成功するチームを作り上げるには、個々のパーソナリティやソフトスキルから生じる行動特性にも目を向ける必要がある。

メルディス・ベルビンによって定義された9つのチームロールを簡単にまとめたものが次のリストとなる。パーソナリティやソフトスキルに基づいた分類であることがよく分かる。

  • Plant(PL) - 非常に創造的であり、斬新なアイデアで問題を解決することを得意とする。
  • Monitor Evaluator(ME) - 偏見を持たず、ものごとを論理的に観察・判断することに長けているため、あらゆる選択肢を公平に評価できる。
  • Specialist(SP) - 特定の領域・分野に関する深い知識や高いスキルでチームに貢献する。
  • Shaper(SH) - 目標達成に向けて精力的に突き進む。チームが常に動き続け、集中力や勢いを失わないようにするための原動力となる。
  • Implementer(IMP) - アイデアを実行可能で効率的な計画に落とし込み、行動に移す。
  • Completer Finisher(CF) - 間違いが無いかをチェックし、納得がいくまで仕上げようとする完璧主義者であり、高品質を追求する。
  • Co-ordinator(CO) - 多くの場合でチームリーダーであり、目標を明確化してチームをその達成に集中させる。
  • Teamworker(TW) - チームの潤滑油的存在であり、外向的で、メンバーが互いに理解し合えるようサポートすることに長けている。
  • Resource Investigator(RI) - 外交的で好奇心旺盛であり、外部から情報を収集し、チームにアイデアを持ち帰る。

1人のチームメンバーが、このどれか1つのチームロールのみに該当するということではない。多くの場合、支配的なチームロールを1つ持ち、副次的なチームロールを2つから3つほど持っている。前者を特に「プライマリチームロール(primary Team Role)」と呼び、後者を「バックアップチームロール(back-up Team Role)」と呼ぶ。メンバーがどのチームロールに該当するかを判別するには専用の質問に回答する必要があるが、自身も含めチームの誰がどれに当てはまりそうか、何となくイメージはできるだろう。

エンジニアになる学生を対象とした調査

『More Effective Agile』内で、ベルビンチームロールに関して参考文献として挙げられているのは、"Optimal selection of team members according to Belbin’s theory" という論文だ。この論文はメルディス・ベルビン本人によるものではないが、エンジニアになる学生を対象とした調査である点が良い。

調査対象となった学生24名は、AからFまでの6つのプロジェクトチームに4名ずつが所属していた。アンケート回答をもとにした彼らの判定結果は次の表の通りとなる(論文中のTable 3を加工)。チームロールの略称が前述のものとは少々異なる。これについては後述する。

列Membersの MF は、男性か女性かを表している。

列CWからCOまではチームロールを表しているが、ここでは9つではなく8つしかない。それは、学生らがそもそもSpecialistに該当する人物像であったためで、これを除く8つのチームロールのみを扱ったようだ。CWとはCompany Workerであるが、これは上述のImplementer(IMP)のことであり、またCHはChairmanで、これはCo-ordinator(CO)の旧名だ。最後のCOは、Completer Finisher(CF)を指す。この8つのチームロールごとに数値が記載されており、数値が大きいほどそのチームロールに対する強度が高いことを表している。

また、チームロール強度は Very high から Very low まで5段階で評価されており、表内ではそれぞれ次のように表現されている。チームロール強度の値がどのランクに該当するかは、チームロールや性別によって異なる(論文内のTable 2を参照)。

  • Very high - 太字/下線/白背景
  • High - 太字/白背景
  • Medium - 白背景
  • Low - 薄い灰色背景
  • Very low - 濃い灰色背景

列Gradeは、プロジェクトに対する最終評価結果を表す。3.0未満は、ネガティブな評価である。

注目すべきは、2.0 という最低のGradeとなったチームBだろう。全チームで唯一、ネガティブな評価となっている。彼らには、最高強度が Low となったチームロールがあるうえに、最高強度が Very high となったチームロールがひとつも無い。それに対して、他のチームはいずれも全てのチームロールが Medium 以上であり、Very high と評価されたチームロールを2つ以上含んでいる。

もう少し詳細に見ると、チームBで Low だったチームロールは、Completer Finisherだ。書籍『Management Teams』によれば、Completer Finisherの欠如は、「目標に手が届きそうなのに、最後の最後で挫折してしまう原因」となる。チームBには「やり抜く力」や「細部へのこだわり」が弱かったのだ。

それに加え、チームBは、Very high の強度を持つCo-ordinatorがいない唯一のチームでもあった。そのために、チーム内でのリーダーシップが弱く、「目標を明確化してチームをその達成に集中させる」ことができなかったのだと考えられる。逆に、High のCo-ordinatorが3名も存在したことによって、チームとして目指すべき方向が統一できなかったのかもしれない。

チームビルディング

ベルビンチームロールの特徴は、なんと言ってもチームに焦点をあてているという点だろう。個人のみに焦点をあてた自己分析・他己分析ツールとはそこが違う。チームにおいて個人がどう効果的に貢献するのかをロールが表現している。したがって、メンバーのチームロールを診断しても、それをチーム設計やチームビルディングに役立てなければ意味がない。

メルディス・ベルビンは成功するチームとして4つのタイプを挙げている。その中でも実験を通じて最も成功したタイプが「混合チーム(classic mixed team)」だと述べている。特定のチームロールに偏った編成ではなく、チームが多くのロールをカバーし、それぞれのロールが明確に区別され、メンバー間でのロールの重複が少ないチームのことだ。

チームワークとチームパフォーマンスを高めるなら、混合チームの実現を念頭にチームを編成するのが良さそうだ。しかし多くの場合において、必要なチームロールを初めからすべて揃えられるほど人材は潤沢ではないだろう。その制約を受け入れた上で、バランスの取れたチームを作り上げていくしかない。

チームビルディングのプロセスは、概ね次のような流れになるだろう。前提として、新規で編成するチームではなく、既存のチームを想定したものとなっている。

(1) チームの現状を把握する - チームメンバー全員が診断を受け、一人ひとりのプライマリチームロールとバックアップチームロールが何であるかを明らかにする。それらをチーム全体で表にまとめ、チームにどのようなチームロールが揃っているのか、何が欠けているのかを可視化し、チームで共有する。

(2) チームをデザインする - チームによって、より優先的に求められる特性が異なる。クリエイティブなことが求められるチームなのか、より正確性や安全性が求められるチームなのか。そういった特性によって、優先的に必要となるチームロールやその強度も異なり、チームロールのバランスも変わるだろう。チームロールをできるだけ広くカバーすることを意識しつつ、チームが担うプロダクトやプロジェクトの特性に合わせてバランスの取れたチームをデザインする。

(3) チームを実装する - (2)でデザインしたチームのあるべき姿と、(1)で明らかにしたチームの現状を比較する。そのうえで、チームデザイン上必要とされたチームロールそれぞれに対し、メンバーのプライマリロールを参考にひとりずつアサインする。ひとつのチームロールに対し、それをプライマリチームロールに持つメンバーが複数人存在する場合、誰がそのチームロールを担うのかを取り決めることで重複を排除する。逆に、必要なチームロールであるが、それをプライマリチームロールに持つメンバーが存在しない場合、各メンバーのバックアップロールを用いてバランスを試みる。1人が複数のチームロールを担うことは問題ない。このように一人ひとりのメンバーが、チームにとっていずれのチームロールとしての行動が求められているのかを明確にしておく。それがピグマリオン効果(ローゼンタール効果)を生み、自らがアサインされたチームロールに対し、よりふさわしい振る舞いを取るようになることが期待できる。なお、ここで決めたチーム編成は、図や表に記録しておく。

(4) チームを稼働させる - (3)で決定したチーム編成を前提に、チームとしてソフトウェアプロダクト開発を続ける。

(5) チームを評価する - 数イテレーション、あるいは数か月ごとに、チームでの振り返りを行う。(3)での実装はうまく機能したか、(2)のデザインは正しかったのかを評価し、問題や課題を明らかにする。そのうえで、(2)あるいは(3)に戻る。

このように、(2)から(5)を繰り返すことで、フィードバックサイクルを回しながらチームのパフォーマンスを高めていくことになるだろう。もちろん、定期的に(1)に立ち戻るのも良い。チームでの経験を積んでいくことで、メンバーそれぞれのプライマリチームロール/バックアップチームロールの組み合わせに変化が起きることもあり得るからだ。

先の論文 "Optimal selection of team members according to Belbin’s theory" 内で、混合チームとしてのスコアを算出する方法が提案されている。ごくシンプルなものであり、カバーするチームロールの数とその強さの加重合計となっている。チームAからFまでのスコアとGradeとの相関も0.87と高い。まだまだ検証が不十分な算出法であると思うが、これを利用してみるのも良いだろう。なお、論文内の実験で考慮されていないSpecialistの扱いをどうするかの検討が必要となる。

選択的同質性からの脱却

マネージャーや企業には、同質性の高い組織を形成しようとする傾向があるのではないだろうか。自身と似た人材や、既存の組織文化との親和性の高い人材を採用し、育てようとする傾向があるということだ。メルディス・ベルビンはこれを「選択的同質性の原則(principle of elective homogeneity)」や「クローン文化」と呼んでいる。それが必ずしも悪いとは言わないが、そのように形成された組織には問題もある。過度な同質性は、選択可能なチームロールを偏らせてしまうからだ。あまりに均一で同質性の高い組織では、最も成功率が高いとされる混合チームのようなチーム編成の実現が難しくなってしまう。

ソフトウェアエンジニアリングは、チームによる取り組みだ。たった1人の突出した能力だけで大きな成功を得ることは難しい。チームによる成功の大きさは個人による成功に勝る。開発チームが高いチームワークをもって、より優れたパフォーマンスを発揮するためには、チームに多様な個性を混合させる必要がある。スーパーエンジニアが目立ってしまいがちでも、実際のところその貢献は、多様な個性によって補い合うチームワークによってもたらされている。

無論、チームであっても、ハードスキルの高さはエンジニアの能力に最も重要な要素のひとつであることに変わりがない。しかし、チームをよく観察すると、ハードスキルが並程度であってもその人の振る舞いがチームを上手くまわす潤滑油になっている人がいたりする。チームロールで言えば、Teamworkerがそれにあたる。

こういったエンジニアは、評価されずに埋もれがちになる。チームの中での彼らの行動は、明確に定義されたものではないため、周囲に意識されないのだ。「名もなき家事」という言葉にかけて「名もなきエンジニアリング」とでも言えば良いだろうか。

チームロールは、そこに名前を付けたのだと考えると良い。デザインパターンやプラクティスと同様に、名前が付けば語彙として共有でき、その行動を誰もが認識できるようになる。チームロールによって、エンジニアをハードスキル以外でも評価することが可能になるのだ。そうすれば、同質性の高い組織から脱却しやすくなる。ある意味で多様性のある組織に変貌することにも繋がるのだろう。

"Products not Projects"で比較される2つのモデルが開発チームを特徴づける

"Products not Projects" は、効果的なソフトウェアプロダクト組織を設計する重要なコンセプトだ。このコンセプトは、James LewisとMartin Fowlerによって2014年3月に公開された記事 "Microservices" の中で、マイクロサービスアーキテクチャスタイルに共通してみられる9つの特徴の1つとして抽出された。

martinfowler.com

その中心にあるのは、開発チームの組織化に関する2つのモデルの対比だ。すなわち、プロダクトに対してチームを組織化するか、プロジェクトに対して組織化するかという選択肢にある。その選択が、サービス品質チームの思考に大きな違いを生み出す。そして、"Products not Projects" という名の通り、プロダクトモデルがより優れた解なのだと説く。

このコンセプトが適用可能な対象は、マイクロサービスアーキテクチャに限定されるものではないと考えている。ソフトウェアプロダクトをビジネスの中核として展開する組織であれば、アーキテクチャに関わらず試す価値のあるものだろう。

本稿ではこのような前提に基づき、"Products not Projects" について掘り下げてみようと思う。

Model - プロジェクトモデルとプロダクトモデル

プロダクトライフタイムに対するチームのカバレッジとの関係性

原文中に1度だけ出現する "lifetime" という単語。これこそ "Products not Projects" を紐解く鍵となる。プロジェクトモデル(project model)プロダクトモデル(product model)では、ソフトウェアプロダクトのライフタイムに対するカバレッジに大きな違いがある。それぞれのモデルを特徴づける最大の要因こそ、そのカバレッジなのだ。

ソフトウェアプロダクトのライフタイムは、並走する2つの活動によって形作られ持続していく。その2つの活動こそが、開発運用だ。2つのモデルのいずれにしても、開発されたソフトウェアプロダクトは、リリースと共に運用に移る。そしてまた次の開発が始まり、新しいバージョンが完成したらリリースされて運用に移行する。これが何度も繰り返されていく。

[Projects] 短命な開発体制

(前略) the aim is to deliver some piece of software which is then considered to be completed.

目的は、完成したとみなされるいくつかのソフトウェアをデリバリすることである。

プロジェクトモデルでは、1度の開発からリリースまでをプロジェクトと呼び、その遂行に対してチームが組織化される。開発が終えるか、リリースが終えればプロジェクトは終了し、チームは解散する。これが、ソフトウェアプロダクトのライフタイムに対するプロジェクトモデルのカバー範囲だ。1つのプロジェクトを、フェーズと呼ばれるいくつかのリリースに分けることもあるが、終わりがある組織であることには変わりはない。このような体制を「プロジェクトチーム(project team)」と呼ぶ。

プロジェクトの成果物であるソフトウェアプロダクトは、リリースと共に運用組織に引き継がれる。いわゆるdevopsが分離している組織構造だ。運用フェーズに移ったソフトウェアプロダクトに対する小さな変更や不具合対応、緊急対応などのソフトウェア保守業務は、専任の保守メンバーが担うことになる。本番環境にアクセスできるのは、保守メンバーを含めた運用組織だけだ。

次のバージョンに向けた大きな開発は、新たなプロジェクトとしてプロジェクトチームが編成される。つまり、dev自体もプロジェクトごとに顔ぶれが変わるのだ。それは、プロジェクトごとにその性質に適した人材配置が行われることによるものだと言いたいところだが、実態はそうでもない。組織内では常に複数のプロジェクトが並走しているのが通常だろうから、「適した人材」のリソースが空いていることなど奇跡だ。だから、人的なリソース計画の都合によって、プロジェクトへのアサインが決定する。

チームのメンバー数も、プロジェクトの規模に応じて様々だ。

[Products] 安定した開発体制

それに対し、プロダクトモデルとはどのようなものなのか。次の引用文は、原文中でプロダクトモデルに関して述べられている箇所だ。

Microservice proponents tend to avoid this model, preferring instead the notion that a team should own a product over its full lifetime.

マイクロサービス推進者たちはこのモデル(=プロジェクトモデル)を避ける傾向にあり、プロダクトのライフタイムすべてに渡ってチームがプロダクトを所有する考え方を好む。

これが、ソフトウェアプロダクトのライフタイムに対するプロダクトモデルのカバー範囲だ。

プロダクトモデルでは、ソフトウェアプロダクトのライフサイクル全てを通し、一貫してチームが責任を負う。チームはソフトウェアプロダクト自体が終わりを迎えることが無い限り解散することがなく、存続し続けることになる。このような体制を「プロダクトチーム(product team)」と呼ぶ。

プロダクトチームは、開発だけでなく運用も担う。devとopsが分離していない。もちろん、より低レイヤーに位置するインフラに関することは、専任のインフラ運用組織が担当する。しかし、本番環境上でソフトウェアプロダクトを動作させ、安定稼働させるのは、プロダクトチームの役割だ。そのために、インフラ運用組織が提供するセルフサービス化されたプラットフォームを活用し、リリースに必要な環境の構築、そこへのアプリケーションのデプロイをはじめ、運用、保守のすべてを行う。

このような運用・保守業務と並行し、プロダクトチームは次のバージョンのリリースに向けた開発も進める。そこで活躍するのはもちろん前回の開発と同じメンバーだ。ソフトウェアプロダクトの長いライフタイムの中でプロダクトチーム内の顔ぶれも少しずつかわっていくかもしれないが、基本的にメンバー構成が一度に大きく変わるようなことはなく、安定している。

その人数も、人が親密な関係を築ける認知的な上限であるダンバー数から、7人前後程度の少人数が良いとされている。この数字は、Jeff Bezosの「2枚のピザ」ルールにも当てはまる。

Knowledges & Skills - ドメイン知識と技術スキルの方向性の違い

フィードバックループとの関係性

"Products not Projects" というコンセプトは、Amazonの "you build, you run it" という考え方をヒントにしたものだ。それは、Amazon.comのCTOであるWerner Vogelsに対するこのインタビュー記事のことだろう。

Giving developers operational responsibilities has greatly enhanced the quality of the services, both from a customer and a technology point of view. The traditional model is that you take your software to the wall that separates development and operations, and throw it over and then forget about it. Not at Amazon. You build it, you run it. This brings developers into contact with the day-to-day operation of their software. It also brings them into day-to-day contact with the customer. This customer feedback loop is essential for improving the quality of the service.

開発者に運用責任を与えることで、顧客と技術の両面においてサービス品質が大きく向上した。従来のモデルでは、開発と運用に隔てる壁までソフトウェアを持っていき、放り投げて忘れ去るものだった。しかしAmazonは違う。開発した人たちが運用も行う。この考えにより開発者は、ソフトウェアの日々の運用に接することになる。顧客とのこのフィードバックループはサービス品質の改善に不可欠だ。

Vogelsが「フィードバックループはサービス品質の改善に不可欠」と話しているように、本番環境で稼働し、顧客に使われるソフトウェアプロダクトからのフィードバックは、サービス品質と強く関係している。チームの実践的な技術スキルを高め、ビジネス、ユーザーに関するドメイン知識を深めさせるからだ。

[Projects] 様々なドメインに対する広い知識と技術面での即応性

Vogelsに対するインタビュー記事からの引用文にある「従来のモデル(traditional model)」とは、プロジェクトモデルのことだ。このモデルでは、本番稼働するソフトウェアプロダクトに対する顧客や保守・運用面からのフィードバックをチームが直接受け取ることができない。リリースすれば、プロジェクトチームは解散してしまうからだ。

プロジェクトモデルであっても、顧客やビジネス担当からの要求などで断続的に新たなプロジェクトが立ち上がる。それらが技術面での改善に向けられたものであることは稀だ。多くは、機能面での追加や改善だ。これによって顧客面でのサービス品質は断続的に改善されていくことになるが、それを実行するチームは要望に応えているだけだ。

その顔ぶれも毎回異なる。例え同じメンバーであったとしても、彼らはプロジェクトとプロジェクトの合間に他のソフトウェア開発プロジェクトに従事している。関わるソフトウェアシステムはそれぞれドメインも違えば抱える課題も異なる。そこで使われる技術も異なるだろう。プロジェクトチームに携わる開発者に求められるのは、様々なドメインに対する広い知識や、技術面での即応性だろう。

[Products] 担当するドメインに対する深い知識と実践的な技術スキル

プロダクトモデルはまさに「開発した人たちが運用も行う(You build it, you run it)」の実践に他ならない。安定したプロダクトチームが開発も運用も担うため、本番稼働するソフトウェアプロダクトからのフィードバックループを形成できる。そこで得られた知識をもってサービス品質を継続的に改善することが可能なのだ。

よく言われるように、開発者が自分たちで保守を担わなければ、その成果物たるソフトウェアに優れた保守性(maintainability)を期待することはできない。開発者は、理解容易性(understandability)変更容易性(modifiability)の低いコードで苦しんでこそ保守性の高いコードを書こうとするようになり、そういうコードを書けるようになる。これもフィードバックループだ。

運用におけるログの大切さもそうだ。適切なログが吐かれていないばかりに緊急対応で苦労してこそ、ログ設計について考えるようになる。そして、システムを安定稼働させるにはどうすれば良いか、問題発生時にシステムを素早く復旧させるにはどうすれば良いか悩み、日々の運用の中で実践を続ける。

これらはいずれも技術面でのフィードバックによって積み重ねた知識と、身につけた実践的技術スキルによって改善されていくサービス品質である。一方でビジネスやユーザー体験に関するサービス品質の向上については、顧客からのフィードバックが重要であることは明らかだ。

ビジネス向け、コンシューマ向けのいずれであるかに関わらず、ソフトウェアプロダクトとは顧客課題、つまりはビジネスやユーザーが抱える何らかの課題に対するソリューションとして作りだされる。ソリューション足り得るかどうかは、実際に顧客がそれらを採用し、利用してくれるかどうか、その結果がどうであったかをみなければ分からない。そこから得られた顧客の声やアクセスログ等は、サービス品質向上に欠かせないフィードバックとなる。

ビジネスやユーザー、技術のいずれであれ、プロダクトモデルであればそれらのフィードバックをチームが受け止めることができる。そこから得た知識や経験が、プロセスやソフトウェアプロダクトの改善につながる。そしてまたフィードバックを得る。このように、プロダクトモデルは強力なフィードバックループを形成でき、プロダクトチームの実践的な技術スキルを高め、担当ドメインの知識を徐々に深めながら、ソフトウェアプロダクトを継続的に改善していくモデルなのだ。

Team Mentality - プロジェクト思考とプロダクト思考

[Projects] 納期・予算・要件の厳守

プロジェクトモデルのゴールは、約束した期日までに、約束した予算で、約束したソフトウェアをリリースすることだ。リリースされたアウトプットが顧客課題のソリューション足り得るかどうかは、最初に決めることであり、ソフトウェアが完成してから変更するものではない。約束通りに作り上げたソフトウェアが結果として十分なソリューションとならず、ビジネスやユーザーにとっての価値を生まなくても、それは必ずしもプロジェクトの責任とは言い切れない。彼らがコミットするのはあくまでも「約束した期日までに、約束した予算で、約束したソフトウェアをリリースする」ことだ。これを「プロジェクト思考(project mentality)」と言う。

もちろん、プロジェクトモデルであっても顧客課題のソリューション足り得ることを目指す。プロジェクトの初期段階において、分析と設計に時間をかけて要件を決めることでそれを担保するのだ。しかし、そうして机上で定義された要件に基づいて完成したソフトウェアプロダクトが、顧客が真に求めるものとなるかは怪しいところだ。Chaos Report 2015によれば、「納期、予算、要件(OnTime, OnBudget, and OnTarget)」という解像度で「成功(successful)」と判断されたプロジェクトは36%であるが、「納期、予算、満足(OnTime, OnBudget, with a satisfactory result)」だと7ポイント低下して29%となっている。

また、分析と設計に多大な時間をかけることは、リリースまでのリードタイムが長くなることも意味する。それに、このアプローチは網羅的な要件と仕様を生み出しやすく、開発スコープを肥大化させるのではないだろうか。プロジェクトサイズが大きいほどプロジェクトの成功率が下がることを、Chaos Reportの調査結果が示している点も見逃せない。サイズがSmallだと「成功(successful)」が61%であることに対し、Grandだと6%しかない。

多くのソフトウェアプロダクト開発が扱う問題領域は、クネビンフレームワーク(Cynefin framework)で言うところの「探索 - 把握 - 対応(probe - sense - respond)」の繰り返しを必要とする複雑(Complex)な領域に位置するのではないだろうか。プロジェクトモデルでのアプローチでは、この問題領域を「把握 - 分析 - 対応(sense - analyze - respond)」で解決する煩雑(Complicated)な領域として扱っている。ソフトウェアプロダクト開発というものを、不確実性は高いが、予測可能性が高く、分析によって問題や解決策が明らかになるものだと想定しているのかもしれない。

mtx2s.hatenablog.com

[Products] 継続的なソリューションの提供

プロダクトモデルは、チームの責任範囲がソフトウェアプロダクトのライフタイム全てに及ぶ。プロダクトチームは、その長い時間の流れの中で、日々の運用や保守を続けなければならない。その間、彼らは顧客や技術面でのフィードバックにさらされ続けることになる。継続的に顧客課題や技術面での問題に直面し、それらを解決するために開発を繰り返す。ソフトウェアプロダクトをより良いものへと漸進的に進化させ続けるのだ。プロダクトモデルにおけるチームは、ビジネスやユーザーにとっての価値に対してコミットしているということだ。これこそが「プロダクト思考(product mentality)」であり、それは次の引用に集約されている。

The product mentality, ties in with the linkage to business capabilities. Rather than looking at the software as a set of functionality to be completed, there is an on-going relationship where the question is how can software assist its users to enhance the business capability.

プロダクト思考は、ビジネス能力との結びつきに紐づく。作り上げようとする機能の集まりとしてソフトウェアを見るのではなく、ユーザーのビジネス能力を向上させるためにソフトウェアがどのように支援できるかという問題に対して、継続的な関係が存在する。

プロダクトモデルでは、プロジェクトモデルのように分析と設計に多くの時間をかけて網羅的な要件と仕様を定義する必要がない。それよりも、実際にリリースしてみる方が効率が良い。リリース後のフィードバックを経て更に改善することを前提に開発できるからだ。ソフトウェアプロダクト開発というものを、不確実性が高いだけでなく、予測可能性が低い対象としてみなしているのだ。つまり、そこで扱われる問題領域を複雑(Complex)な領域と位置づけ、反復的な「探索 - 把握 - 対応(probe - sense - respond)」というやり方を実践している。

このような開発スタイルであるから、プロダクトモデルでの1度の開発規模はプロジェクトモデルに比べて相対的に小さく、リリース頻度も高い。先述したChaos Reportの調査結果にあるように、開発規模が小さい(small)なら、高い成功率も期待できるのではないだろうか。

プロダクトモデル実践における落とし穴

このようにして2つのモデルを比較してみると、プロダクトモデルのより優れた面が理解できる。プロダクトモデルを実践すれば、頭を悩ます様々な問題が解決し、成功に向けてものごとが上手く動き出すように感じてしまうかもしれない。そうしてプロダクトモデルを始めてみると、しばらくして現実はそう甘くないと痛感することになるだろう。そこには様々な落とし穴があるからだ。

まず、チームが運用を担うということは、運用負荷が高いほど開発時間が削られてしまうということでもある。運用負荷を下げるためには、それを実現するための仕組みが必要であり、運用の自動化に向けた開発といった取り組みを進めなければならない。

構築やデプロイの自動化をはじめ、ビルドパイプラインの整備、データなどのバックアップ処理、ログ収集・可視化の仕組み、モニタリング環境の整備など、運用負荷を下げるためにやるべきことは、これらも含めて他にも色々と存在する。プロダクトチームは、専任のインフラ運用組織とも協力(DevOps)して仕組みを作り上げ、維持することになるだろう。

しかしプロダクトチームは、短期間で繰り返されるリリースの中で、開発業務に追われ、運用負荷を下げるためのタスクに取り組むことがなかなかできずにいることが多い。そのために、運用業務にも時間が取られ、開発業務に必要な時間がますます逼迫して、運用負荷を下げる試みに手が出ないという悪循環に陥ってしまう。時間の無い中での開発は、プロダクトコードの品質にも悪影響があるだろう。

プロダクトモデルはサービス品質を高めるはずだが、このような状況は逆にサービス品質の低下を招きかねない。すると、ソフトウェアシステムが頻繁にトラブルを起こし、その緊急対応に追われるようになる。開発者はその都度、仕掛中の開発の手を止めることになり、開発時間が削られるだけでなく、開発に集中することすらままならなくなる。

こういった事態を回避するヒントは、SRE(Site Reliability Engineering)の「50%ルール」だろう。運用業務に費やす時間を業務時間全体の50%以下にし、それを超過した場合は運用負荷を下げるタスクに費やすというものだ。プロダクトモデルにおけるチームでも、運用業務に費やす時間を計測し、20%といった閾値を設けると良いのではないだろうか。

プロダクトモデルを実践する上での落とし穴は他にもまだまだ存在する。その代表的な例を挙げる。

  • 属人化による業務負荷の偏り:トラブル対応や問い合わせ対応といった運用業務は、特定個人に集中しやすい。それが、チーム内の業務負荷を偏らせる。さらに、この状況が長く続くと属人化を生み、業務負荷の平準化を困難にするとともにリスクにもなる。退職などでチームがその個人に頼ることができない状況に陥ると、途端に業務がまわらなくなるからだ。
  • 価値観の固定化:流動性の低い安定したチームは、チーム内での価値観が固定化しやすく、新しい価値を生み出しにくくなる体制でもある。チームがそのような状態にあると、ソフトウェアプロダクトもチーム自身も進化の歩みが鈍化し、イノベーションを期待することなどできなくなる。安定したチームが良いと言っても、ある程度の流動性は必要だろう。
  • 悪いコンフォートゾーン:長いあいだ同じ顔ぶれで仕事を続けると、相互信頼の醸成と共に、チーム内に甘えや妥協がはびこるようになる。私はこれを「悪いコンフォートゾーン」や「勘違いの心理的安全性」と呼んでいる。チームが成長するためには、コンフォートゾーンから抜け出し、勇気を持ってラーニングゾーンで挑戦を続けなければならない。
  • プロダクト思考への不達:開発業務に追われている限り、チームがプロダクト思考を獲得することはない。約束した期日までに、約束した予算で、約束したソフトウェアをリリースすることにコミットするプロジェクト思考にとどまってしまう。なんとかリリースしたらそこで終わり。そのアウトプットのことは忘れ、次のリリースに集中する。だからフィードバックループが形成されないのだ。この状態に陥ることを回避することや、既に陥ってしまったチームの問題を解決することは最も難しい。

まとめ

プロダクトモデルは、ソフトウェアプロダクトのライフサイクル全てを通し、一貫してチームが責任を負う。その長い期間の中で、少人数で安定したプロダクトチームがその運用と繰り返される開発を担う。その体制とプロセスがフィードバックループを形成し、チームのドメイン知識を深め、実践的な技術スキルを高めながら、サービス品質の継続的な改善を可能にする。チームはソフトウェアプロダクト開発が扱う問題領域を複雑な領域と位置付け、不確実性が高いだけでなく、予測可能性が低い対象としてみなしている。これら全てがチームをプロダクト思考に導いていき、チームとソフトウェアプロダクトを、改善を超えた進化へと突き進める。これが "Products not Projects" というコンセプトだ。

このコンセプトにおけるプロダクトモデルは、プロダクト思考を伴ってこそビジネスの成功を引き寄せる。体制やプロセスを真似たところで、チームがプロダクト思考を獲得するわけではない。そこには様々な落とし穴がある。プロダクトモデルを真に実践するためには、チーム自身や彼らをサポートするマネージャーが強い意思を持ってプロダクト思考に近づけようとする努力が必要なのだ。その難易度は高いが、到達したその見返りは想像以上に大きいだろう。

mtx2s.hatenablog.com

note.com

コンウェイの法則と、そこで提示された2つの組織課題

ソフトウェアエンジニアリング関連の書籍を読んでいると、「コンウェイの法則(Conway's law)」によく出会う。その引用元は、1968年4月に発表されたメルヴィン・コンウェイ(Melvin E. Conway)の論文 "How do committees invent?" で、例の有名な一文は結論(conclusion)に書かれている。

(前略) organizations which design systems (in the broad sense used here) are constrained to produce designs which are copies of the communication structures of these organizations.

(広義での)システムを設計する組織は、自らのコミュニケーション構造を真似た設計を生み出すという制約を受ける。

論文の内容はこの一文に集約されるのであるが、それに付随して2つの重要な組織課題を提示している。それは、「コミュニケーションの効率化」と「生産性向上を人数規模に頼らないマネジメント哲学の構築」だ。本稿ではこの2つの課題を中心に、コンウェイの法則について深堀りする。

コンウェイの法則とは

まず、先述した2つの課題の根幹にあるのは、システムが、それを設計する組織の準同型写像(homomorphism)であるという点だ。これこそコンウェイの法則そのものだ。課題について考察する前に、この点について理解を深めておきたい。

システムは、基本的に構造化されているものだ。それは、相互に接続されたより小さな要素からなり、その要素もまた、さらに小さな要素から構成されている。ソフトウェアシステムで言えば、この要素とは、サービスやコンポーネント、モジュールであり、最終的にはクラスやメソッドといったレベルの詳細度で構成される。これらはインタフェースを介して接続されている。

そのようなシステムを設計する組織もまた、構造化されている。組織は複数のグループに分かれており、グループは更に小さなグループや、さらには複数のメンバーで構成されている。ここではグループのことをチームと呼ぼう。システムでは要素がインタフェースで接続されていたわけであるが、このチーム間、あるいはチーム内を接続するのは、コミュニケーションパスである。

コンウェイの論文では、システムと組織が持つこのような構造が一致することを指摘している。

システム構造内で互いに接続された要素xとyがあれば、組織構造内にもそれを設計した要素XとYがインタフェース仕様を合意しているはずだ。そこにはコミュニケーションパスが存在する。逆に、システム要素xとyの間に通信が無いならば、組織要素XとYの間にもコミュニケーションパスが存在しない。

ここで、組織要素がそれぞれ複数のシステム要素を設計したなら、組織構造は、システム構造を折りたたんだものになる。このような関係を、準同型写像と呼ぶ。下図は、その説明に使われた論文内のものだ。

さて、人数規模にもよるが、組織内のそれぞれのチームやメンバーが、他の全てのチームやメンバーに対して偏りなくコミュニケーションパスを持つことはまず不可能だろう。コミュニケーションパスが存在しなければ、互いが担当するシステム要素を接続することはできない(あるいは、積極的には接続しようとはしないかもしれない)。したがって、組織構造によって、採用できない設計があり得ることになる。論文内で結論(conclusion)に記載されたコンウェイの法則と呼ばれる一文に、"constrain" という単語が用いられているのは、設計において選択可能な選択肢に対するこのような「制約」が存在するからではないだろうか。

課題1:コミュニケーションの効率化

論文内の印象的な提言として、次の一文がある。

Even in a moderately small organization it becomes necessary to restrict communication in order that people can get some "work" done.

適度に小規模な組織であっても、人々が仕事をやり遂げるには、コミュニケーションを制限する必要がある。

これは、どういうことだろうか。

よく言われるように、コミュニケーションコストは、人数に対する2乗のオーダーで増加する。n人の組織では、1人あたり最大 n-1 個のコミュニケーションパスを持つことになる。自分以外の全員とパスを持つという前提だ。そうすると、組織全体のパス総数の最大は、n*(n-1)/2 となる。2で割っているのは、パスが重複するからだ。

一方で、人数に対する組織の純粋な処理能力の伸びは線形だ。グラフで描くと分かるように、人数が増えるにつれ線形に伸びる処理能力に対し、コミュニケーションコストは放物線を描く。

組織の処理能力の一部は、このコミュニケーションコストによって消費されることを忘れてはいけない。2つの線に挟まれた距離が、組織の実質的な処理能力なのだ。したがって、増員による組織の処理能力向上という目論見はどこかの時点で破綻してしまう。コミュニケーションを制限する必要性は、このようなコミュニケーションコストを抑制する目的によって生じるのだ。

これらのことから、コミュニケーション効率を踏まえた上での組織設計が重要なのは明らかだ。下記は、その点について論文内で言及された箇所の引用だ。

Research which leads to techniques permitting more efficient communication among designers will play an extremely important role in the technology of system management.

設計者間のコミュニケーションをより効率的にする技術を研究することは、システムマネジメント技術において、非常に重要な役割を果たす。

コミュニケーションが非効率であるほどコスト放物線が描くカーブは極端になる。それは、組織の人数規模に対し、コミュニケーション構造の崩壊をより早めることを示している。そして、組織のこのようなコミュニケーション構造の崩壊は、システム構造の崩壊につながるのだ。

課題2:生産性向上を人数規模に頼らないマネジメント哲学の構築

コンウェイの論文は、半世紀以上も前に発表されたものだ。前提などに古さを感じる箇所もあるが、それでも今なお引用され続けることからも分かる通り、価値ある文書だ。そこでは、マネジメントの問題についても触れられている。それは現在でも見かける問題ではないか。

その問題とは、プロジェクトにおいて、可能な限りすべての人的リソースを投入することなくスケジュールに間に合わなかった場合、マネジメント上の問題としてマネージャーが非難を受ける可能性があるというものだ。プロジェクトの人的規模を拡大することが適切な解決策ではないとしても、そのような不合理にさらされたくないと願うマネージャーは、人をより多く投入するという選択を行ってしまう。扱うシステムの規模が大きいほど、発生しやすい問題だと言える。

このような不合理が生じる背景は、まさに『人月の神話』にあるのだろう。5人で10か月かけて完成させる仕事を、20人で2.5か月に圧縮して完成させようと考えてしまう罠だ。コストという観点では、どちらもともに50人月だ。しかし、人と月とは交換可能ではない。設計における個々のタスクは独立しておらず、多くのタスクが互いに従属関係を持つ。それらがスケジュール上にクリティカルパスを形成する。クリティカルパスは、人を増やしても短くならない。単純に人数を増やしたところで仕事が早く終わるわけではないのだ。

そして、この設計労力に対する人的リソースの過大配分には、「パーキンソンの法則(Parkinson’s law)」も関係している。利用可能な人的リソースを使い切るまでプロジェクトの人数規模は膨張する傾向があるのだ。プロジェクトに投入可能な人員を多く抱えるマネージャーほど、この罠に陥りやすい。

そもそも、労働力として同じ50人月の価値の組織であっても、そこから生み出されるシステムの価値は同一とはならない。コミュニケーション構造が違えば、生み出されるシステム構造は準同型写像によって違いが生じるからだ。そして、無理に人手を増やしすぎた結果は、課題1で述べた通りだ。

このような問題に対し、論文では組織のスリム化と柔軟性の重要性を説いた上で、次のように述べている。

There is need for a philosophy of system design management which is not based on the assumption that adding manpower simply adds to productivity.

「人手を増やせば単純に生産性が上がる」という思い込みに基づかないシステム設計マネジメントの哲学が必要だ。

適切なシステム構造は価値である

コンウェイの法則が、これほどまでに様々な書籍やドキュメントで引用される背景には、システムの構造というものに関心を持つ人や組織が多いからではないだろうか。システムを扱う組織にとって、適切なシステム構造は価値あるものなのだ。フィリップ・クルーシュテン(Philippe Kruchten)は、それを「見えない(invisible)が、ポジティブな価値(positive valueがあるもの」と呼んだ。そしてその逆となるものを「技術的負債(technical debt)」として仕分けした。

技術的負債となるシステム構造の崩壊は、変更容易性を悪化し、ソフトウェアに対して顧客に要求される変更を実現困難にする。つまり、左象限の「見える価値(visible value」を受け付けなくなる。そうなったソフトウェアは、もはや "ソフト" ではない。変更できないソフトウェアは、ソフトウェアとしての価値、存在意義が失われてしまっている。

システム構造は、組織のコミュニケーション構造に強い影響を受ける。忘れてはならないのは、ソフトウェアプロダクト開発組織におけるコミュニケーションパスの有無やそのパスの太さは、それぞれが担当するコンポーネントの依存関係に強く影響されるということだ。つまり、アーキテクチャを想定せずに設計された組織のコミュニケーション構造は、不適切で非効率なものとなる。そうであるならば、効率的なコミュニケーション構造の追求のために、組織設計者にはアーキテクトとしての素養が不可欠だと言えるのではないだろうか。

マイクロソフトの調査にみるコードのオーナーシップと品質の関係

ひとつのソフトウェアコンポーネントが多くの開発者によって変更されると、品質に悪い影響を与えると経験的に感じている。設計に一貫性が失われることや、知識の浅い状態で変更することによるバグ混入の可能性が高まるからだ。

2011年9月に公開されたマイクロソフト社の調査結果、"Don’t Touch My Code! Examining the Effects of Ownership on Software Quality" は、この「コードのオーナーシップはソフトウェアの品質を左右する」という経験則を裏付けるものだった。全体のコミット数のうち5%未満の貢献にとどまる開発者が多いコンポーネントは、リリース前後における故障が増加するというものだ。

本稿では、このマイクロソフトによる調査結果を紹介し、それを踏まえた上で、ソフトウェアプロダクトの品質悪化を抑えるための組織やプロセス、アーキテクチャについて簡単に考えてみる。

用語:コンポーネントのコントリビューターとオーナーシップ

さきに用語についてであるが、特に重要なものは次の4つだ。

  • コントリビューター(Contributor)
  • オーナーシップ率(Proportion of Ownership)
  • メジャーコントリビューター(Major Contributor)
  • マイナーコントリビューター(Minor Contributor)

この調査では、コンポーネントを変更/コミットした人を、そのコンポーネントに対する「コントリビューター」と呼んでいる。

コンポーネントXに対するコントリビューターAのコミット数が、X全体のコミット数の何パーセントにあたるかを表すメトリクスは、「オーナーシップ率」あるいは単に「オーナーシップ(ownership)」と呼ぶ。

オーナーシップが5%以上のコントリビューターを特に「メジャーコントリビューター」と呼び、5%未満では「マイナーコントリビューター」と呼んで区別している。

結果:マイナーコントリビューター数が故障数に大きな影響を与える

調査では、Windows VistaWindows 7という、2つの大規模ソフトウェアプロジェクトを対象としている。下の表は、それらのリリース前後の故障と各メトリクスとの相関分析結果となる。表内の数値が相関係数で、1に近いほど両者の間に強い正の相関があり、一方が大きくなると、もう一方も大きくなる関係を表す。また、-1に近いほど強い負の相関があり、一方が大きくなると、もう一方は小さくなる。

Ownership Metrics に分類されている TOTAL, MINOR, MAJOR, OWNERSHIP はそれぞれ、コントリビューター数、マイナーコントリビューター数、メジャーコントリビューター数、オーナーシップのことだ。OWNERSHIP には、それぞれのコンポーネントの最大コミット数を持つコントリビューターのオーナーシップが使われている。Classic Metrics に分類されている Size, Churn, Complexity は、静的コード解析でおなじみのコード品質に関するメトリクスだ。

Windows Vistaの結果を見ると、Ownership Metrics のうち、MAJOR を除いたいずれのメトリクスも、リリース前後の故障と強い相関関係がある。トータルのコントリビューター数やマイナーコントリビューター数が多いコンポーネントは故障が多く、オーナーシップが高いコンポーネントは故障が少ない傾向が強い。これは、概ね期待通りの結果だろう。Windows 7のリリース前も同様の傾向だ(リリース後の相関係数が小さくなっているのは、この時点ではリリース後の故障に関する報告が集まってなかったからのようだ)。

ただし、この結果だけで結論は出せない。コード品質との間でも強い相関を示しているからだ。例えば、Windows Vistaでは、TOTAL とリリース前の故障との相関係数が 0.84 となっているが、Size の相関係数も 0.75 と高い。コントリビューター数とサイズの間には関係があるだろうことは想像がつく。つまり、Ownership Metrics の各相関係数は、Classic Metrics に影響を受けた可能性があるということだ。

そこで、故障を目的変数とする多重線形回帰分析を行い、Classic Metrics のみを説明変数に含むモデルと、そこに Ownership Metrics の各種メトリクスを順に加えていったモデルを比較した結果が次の表となる。表内の数値は、故障数の分散のどの程度がメトリクスによって説明されるかを示している。

Base が、Classic Metrics のみを含むモデルだ。このモデルでは、Windows Vistaのリリース前の故障を 26% 説明できる。Base+TOTAL は、Base に対してコントリビューター数を説明変数に加えたモデルで、Windows Vistaのリリース前が 40% となり、Base と比較して 14% 改善していることが分かる。同様に、Base+MINOR のリリース前を見ると 46% であり、Base より 20% 改善し、Base+TOTAL より 6% 高い。つまり、コード品質を考慮してもなお、マイナーコントリビューター数がWindows Vistaのリリース前故障数に強い影響を与えていることが分かる。その影響の度合いは、トータルのコントリビューター数を使った故障数の予測より 6% 高い。

MAJOR や OWNERSHIP を追加したモデルも、Base+MINOR よりわずかな改善を示している。しかし表には記載されていないが、Base+MAJOR や、Base+OWNERSHIP に対して MINOR を追加したモデルは、追加前のモデルより大きな効果を示したようだ。つまり、メジャーコントリビューター数やオーナーシップより、マイナーコントリビューター数の方が、Windows Vistaのリリース前の故障数をより説明できるということだろう。

これらの傾向は、Windows 7のリリース後の故障を除けば、いずれもWindows Vistaのリリース前の故障と同じような傾向を示している。

以上のことから、マイナーコントリビューター数やトータルのコントリビューター数は、リリース前後の故障数に大きな影響を与えると言える。メジャーコントリビューター数や、オーナーシップもリリース前後の故障に影響を与えるが、その度合はマイナーコントリビューターやトータルと比べるとかなり小さい。

故障数を抑えるために何ができるのか?

どうすれば、マイナーコントリビューター数やトータルのコントリビューター数を低く抑えられるだろうか。

まず考えられるのは、ひとつひとつのコンポーネントのサイズを小さくしておくことだろう。サイズが大きいと、コードの変更に関わる開発者の人数も増える。そもそも、サイズが大きいという点からも品質が悪化する恐れがある。

コンポーネント同士の結合度を下げることも必要だ。マイクロソフトの調査結果では、あるコンポーネントのメジャーコントリビューターが、そのコンポーネントと依存関係にある別のコンポーネントのマイナーコントリビューターになることも明らかにしている。結合度を下げることができれば、コンポーネントをまたいだ変更の必要性も下げられるということだ。

また、結合度の強いコンポーネント同士については、同一の開発者がオーナーとなるよう組み合わせるのも良いだろう。そうすれば、オーナーとなった開発者は、どちらのコンポーネントにおいてもメジャーコントリビューターになるはずだ。

チームによるコンポーネントのオーナーシップ

このように整理していくと、安定した少人数の開発チームが特定のコンポーネントを所有するやり方が合理的であることがよく分かる。そうすれば、コンポーネントの変更に関わる開発者の人数も少なくなり、その顔ぶれも基本的に限定される。担当するコンポーネントに関する知識も深まるだろう。マーティン・ファウラー(Martin Fowler)の言葉を借りると、チーム内のメンバー同士は「コードの共同所有(collective code ownership)」であり、それぞれのチームとしては「強いコードの所有(strong code ownership)」だと言える。前者はエクストリーム・プログラミングのプラクティス「コードの共有(shared code)」の実践でもある。

フィーチャー開発において同時に変更されることが多いコンポーネントは、なるべく一括して同じチームが所有する。これなら、依存関係によって発生するようなコンポーネントをまたいだ変更も、チーム内で行える。アーキテクチャによっては、コンポーネントテストやデプロイの容易性が高まり、チーム間でのデリバリー衝突も避けやすくなるだろう。

こうしてひとつのチームに集められた複数のコンポーネントは、ビジネス機能を境界としたコンテキストを形成するはずだ。あるいは、コンテキストは異なっていても、ドメイン間の結合度が高いコンポーネントが集まる。サム・ニューマン(Sam Newman)も言うように、機能の変更とは、ビジネス機能の変更を指すからだ。もし、ビジネス機能ではなく、技術視点でコンポーネントを集めてしまうと、チームの所有権をまたいだコードの変更が頻発するようになる。これは、コストがかかる上にマイナーコントリビューターを生み出しやすい構造だ。

しかし時には、チームが抱えるバックログの実現のために、自チームが所有するコンポーネントのみならず、他チームが所有するコンポーネントを変更するケースも発生し得る。他チームのコンポーネントを変更する開発者は、マイナーコントリビューターだ。こういった場合、コンポーネントを所有する側のチームが、マイナーコントリビューターによって変更されたコードをしっかりとレビューする必要がある。これについての詳細は、過去の記事『開発組織を分散モノリスにしないチーム分割と協働のデザイン - mtx2s’s blog』の中で、「コントリビューター/コミッターモデルでのフィーチャー開発と協働」として書いた。

mtx2s.hatenablog.com

あまりにコンポーネントの数が多いと、どうしてもオーナーシップが低くなってしまうコンポーネントもでてくる。調査結果でも提言があるように、そういったコンポーネントを変更するときは、QAによる検証の優先度を上げるといった工夫も必要になるだろう。

最後に

はっきり言って、この調査で得られた結論に大きな意外性はなかった。しかし、経験的に理解していたことが、統計的に明らかになったという点は大きいと言えるだろう。

マイクロソフトの調査は、多くの企業の多くのソフトウェアプロジェクトを対象としたものではなく、Windos VistaWindows 7という、自社のソフトウェアプロジェクトに限定されていた。したがって、調査結果をそのまま様々なプロジェクトに一般化できるとは限らないだろう。しかし少なくとも、比較的多くの人が関わるソフトウェアプロダクト開発を抱える組織では、この結果を上手く活用できると考えている。

ArchUnitでアーキテクチャをテストする

ソフトウェアアーキテクチャには、依存関係のデザインという側面がある。その目的は多くの場合において、ソフトウェアの振る舞いに対する変更容易性を高めることではないだろうか。

ソフトウェアプロダクトは、そのライフサイクルを通して、繰り返し変更し続けられていく宿命にある。それがユーザーや顧客の要求であり、彼らの価値につながるからだ。そしてその提供には迅速さも求められる。依存関係のデザインは、これを実現するために組み込まれたソフトウェアの構造なのだ。

アーキテクトの悩みのひとつは、このような目的に基づいて自らがデザインしたソフトウェアの構造が、儚く崩れ去っていくことだ。アーキテクチャとは所詮はルールでしかない。開発チームが厳密にルールを守らない限り、望ましい構造を構築・維持することはできない。アーキテクチャは脆いのだ。それこそが技術的負債の一因でもある。

無償のJavaライブラリとして提供されているArchUnitは、そのルールをコードとして表現することを可能にした。テストオートメーションの一部としてビルドパイプラインに組み込めるということだ。これは、開発チームがデリバリするソフトウェアプロダクトの構造を、アーキテクトが描いたアーキテクチャに誘導し続ける大きな力となる。

私自身もまだ、ArchUnitの導入に向けた技術的な調査段階であるが、大いに可能性を感じている。本稿では、その過程で知ったArchUnitの機能について、ユーザーガイドを参考にしつつ紹介していく。

なお、本記事で対象としたArchUnitのバージョンは以下の通りとなる。

com.tngtech.archunit:archunit:1.0.0-rc1
com.tngtech.archunit:archunit-junit5:1.0.0-rc1

ドキュメントとしてのテストコード

現実問題として、人が増えれば増えるほど、アーキテクチャとして描いたビジョンの浸透は難しくなる。その上、ソフトウェアエンジニアのスキルレベルが玉石混交となりやすい。少数精鋭の時のようにはいかない。以前の記事でも書いたが、様々な要因で積み重なる技術的負債の中でもこういった背景による負債こそが問題だと私は考えている。

mtx2s.hatenablog.com

次の図は、マーティン・ファウラー(Martin Fowler)による技術的負債の発生理由に関する四象限(TechnicalDebtQuadrant)を日本語化したものだ。上述した負債は、この四象限の左下にあたる「無謀で無自覚な負債」にあたる。

ArchUnitによるテストコードがあれば、意図的か無自覚かに関わらず、「無謀な負債」の蓄積を抑止・軽減できる。ルールに沿わない構造を持つ変更は、ビルドを失敗させるからだ。その原因を作った開発者は、当然ながらどんなテストが失敗したのか調べることになる。そうすることで、アーキテクチャがどのように定義されているのか学ぶことにもなる。

ここで発揮されるのが、ArchUnitで定義されたルールの可読性だ。次の例のように、英語の文章としてそのまま読めるメソッドチェーンを形成する構造となっている。classes() は、ArchRuleDefinition クラスから import static したメソッドだ。

ArchRule myRule = classes()
    .that().resideInAPackage("..service..")
    .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..");

日本語で言えば、「service パッケージに存在するクラスは、controller および service パッケージからのみアクセスされるべき」といったところか。"..service.." は、com.example.myapp.servicecom.example.myapp.service.customer などのパッケージ名にマッチするパターンだ。詳しくは PackageMatcherドキュメント(コメント)に記載されている。

ところで、ArchUnitに関わらず、テストコードは何が良くて何が駄目なのかを明確に表現するドキュメントとなるが、なぜ良くて、なぜ駄目なのかまでは示さない。テストコードはあくまでもwhatを示すもので、そのhowがプロダクションコードだと言えるだろう。一人で開発する分にはそれだけでも十分であるが、複数人で開発するならやはりwhyが必要だ。テストコードやプロダクションコードだけに頼らず、コメントなどでwhyをしっかり残すことも必要だろう。

基本ステップ

ArchUnitでのコーディングの基本ステップは次の通りだ。

  1. 検査対象とするクラス群をインポートする
  2. ルールを定義する
  3. ルールを使い、インポートしたクラス群を検査する
JavaClasses classes = new ClassFileImporter().importPackages("com.example.myapp");

ArchRule myRule = classes().that().resideInAPackage("..service..")
    .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..");

myRule.check(classes);

この例では、パッケージ名を指定してインポートしているが、他にもURLやJarファイルを指定することもできる。これらのバリエーションについては、ClassFileImporter クラスのメソッドを眺めると把握できるだろう。いずれにしても、最終的には importLocations(Collection<Location>) メソッドが呼び出されるようだ。

ちなみに、JUnit5でのテストは次のように書ける。

@AnalyzeClasses(packages = "com.example.myapp")
public class ServiceTest {
    @ArchTest
    static final ArchRule service_only_be_accessed_by_controller_and_service =
            classes().that().resideInAPackage("..service..")
                .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..");
}

こちらも @AnalyzeClasses アノテーションに用意された属性を見てみると、packages 以外にも packagesOflocations などいくつかのインポート手段があるようだ。

どんなことができるのか

レイヤードアーキテクチャ(Layered Architecture)クリーンアーキテクチャ(Clean Architecture)といった数々のソフトウェアアーキテクチャパターンが示すように、それらが持つ構造は、責務に注目してソフトウェアを論理的あるいは物理的な境界で分離し、高凝集疎結合モジュールクラスコンポーネントを実現するものだ。このようなアーキテクトによる構造定義に基づいて、開発者はソフトウェアプロダクトを実装し、成長させていく。

たいていのプログラミング言語名前空間(namespace)を備えており、それをモジュールの単位として扱う。Javaで言えばパッケージ(package)がそれにあたる。ではモジュールたるパッケージに関する構造をArchUnitでどのように定義するのか、簡単な例をみてみる。

例1:foo パッケージ内のクラスは、bar パッケージ内のクラスに依存してはならない

noClasses().that().resideInAPackage("..foo..")
    .should().dependOnClassesThat().resideInAPackage("..bar..")

例2:foo パッケージ内のクラスに対するアクセスを、foo パッケージ自身と、bar パッケージに限定する

classes().that().resideInAPackage("..foo..")
    .should().onlyBeAccessed().byAnyPackage("..foo..", "..bar..")

例3:foo パッケージ内のクラスに対する依存を、foo パッケージ自身と、bar パッケージに限定する

classes().that().resideInAPackage("..foo..")
    .should().onlyHaveDependentClassesThat().resideInAnyPackage("..foo..", "..bar..")

この、例2と例3の違いは、「アクセス」と「依存」だ。例2では、フィールドやメソッド、コンストラクタに対するアクセスのみ禁止しているだけだが、例3では依存関係を作ること自体を禁じている。このような細やかな制御が可能である点も気が利いている。

もちろん、パッケージを指定したルールだけではなく、次の例のようにクラスを指定したルールを記述することもできる。

例4:Fooassignable なクラスに対する依存を、foo パッケージと、bar パッケージに限定する

classes().that().areAssignableTo(Foo.class)
    .should().onlyHaveDependentClassesThat().resideInAnyPackage("..foo..", "..bar..")

例5:名前が Foo で終わるクラスは、foo パッケージ内に入っていなければならない

classes().that().haveSimpleNameEndingWith("Foo")
    .should().resideInAPackage("foo")

以上のように、多くのルールは次の形式で定義される。

classes that ${PREDICATE} should ${CONDITION}

PREDICADEとCONDITIONのバリエーションについてはそれぞれ、ClassesThat インタフェース、ClassesShould インタフェースが持つメソッドを見ると良い。ここで例に挙げた表現がほんの一部でしかないことがわかる。

これらの例ではいずれも classes() を使ってクラスを対象にしたルールを表現していたが、次の例の methods() のようにメソッドを対象にしたり、フィールドやコンストラクタなども対象にすることができる。これらのバリエーションは、ArchRuleDefinition クラスのメソッドとしてまとまっている。

例:foo パッケージ内にあるクラスの public メソッドは、@Bar アノテーションが付けられていなければならない

methods().that().arePublic().and().areDeclaredInClassesThat().resideInAPackage("..foo..")
    .should().beAnnotatedWith(Bar.class);

個人的な印象として、Javaでの厳密なモジュール化(modulization)の実現は難しいと感じている。モジュールレベルでのカプセル化を実現するために、公開する振る舞いを制限しようにもアクセス修飾子だけでは不十分だったし、それを補おうとするとクラス同士の関係やコードが複雑になり過ぎてしまう。苦労して実現したとしても、モジュールユーザーによってコードレベルで破られることもある。いずれにしても、何らかの形でカプセル化が破られてしまうと、モジュールへの変更に対するモジュールユーザーへの影響が拡大してしまう。それらを防ぐ防御策はこれまでずっと、文書と規律という不確実な手段にゆだねられてきた。

ArchUnitは、カプセル化の堅持をテストコードによって保証する。規律に強制力が伴ったのだ。カプセル化を追求し過ぎることによる複雑化の軽減も期待できそうではないか。

定義済みの頻出ルール

ArchUnitの主要なAPIは、Core, Lang, Libraryの3つのレイヤーに分かれている。CoreレイヤーのAPIは、基本的な機能を提供している。LangレイヤーのAPIは、ここまで例に挙げたような、アーキテクチャを簡潔に定義するためのルール構文を提供している。そしてここで取り上げるLibraryレイヤーは、よく使われるであろうパターン化されたルールを簡単に扱うためのAPIを提供している。

例えば、非循環依存関係の原則(ADP, Acyclic Dependencies Principle)を徹底したいならば、次のように書くだけだ。

slices().matching("com.example.myapp.(*)..")
    .should().beFreeOfCycles()

この例では、com.example.myapp 配下におけるパッケージ間の循環依存を検出できる。slices() は、SlicesRuleDefinition クラスのメソッドを import static したものだ。

DependencyRules クラスの定数 NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES も興味深い。これは、下位階層パッケージのクラスが上位階層パッケージに直接依存することを禁止する ArchRule だ。

他にも、GeneralCodingRules クラスや ProxyRules クラスにも定数やメソッドがいくつか定義されている。NO_CLASSES_SHOULD_USE_JODATIMEUSE_JODATIME など、細かいがなかなか気が利いている。

おそらく最も注目すべきなのは、典型的なアーキテクチャパターンが、Architectures クラスに定義されていることだろう。

例:レイヤードアーキテクチャ(Layered Architecture)

layeredArchitecture()
    .consideringAllDependencies()
    .layer("Controller").definedBy("..controller..")
    .layer("Service").definedBy("..service..")
    .layer("Persistence").definedBy("..persistence..")
    .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
    .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
    .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")

説明するまでもないコードだが、見ての通り3つのレイヤーを定義した上で、それぞれ閉鎖レイヤーにするか、開放レイヤーにするかといったレイヤーの分離を定義している。

例:オニオンアーキテクチャ(Onion Architecture)

onionArchitecture()
    .domainModels("com.myapp.domain.model..")
    .domainServices("com.myapp.domain.service..")
    .applicationServices("com.myapp.application..")
    .adapter("cli", "com.myapp.adapter.cli..")
    .adapter("persistence", "com.myapp.adapter.persistence..")
    .adapter("rest", "com.myapp.adapter.rest..");

こちらも見た通りだ。アプリケーションコア(application core)となるドメインモデル(domain model)ドメインサービス(domain service)アプリケーションサービス(application service)の3レイヤーを定義した上で、アダプタ(adapter)を定義している。

1.0.0-rcでのサポートは、レイヤードアーキテクチャとオニオンアーキテクチャの2つのみのようだが、今後の追加に期待できそうだ。

既存プロダクトへの導入時に起こりうる大量のルール違反

レガシーなソフトウェアプロダクトに対してArchUnitを使い始めれば、おそらく大量のルール違反が出てしまうだろう。そういったケースでの対応方法も用意されている。

そのひとつは、archunit_ignore_patterns.txt という名前のファイルをクラスパスに置くことだ。このファイル内に行区切りで記述された正規表現にマッチした違反は無視されるようになる。

例えば次のように書けば、com.example.myapp.foo.Foo クラスに関する全ての違反が無視され、失敗ではなく成功が報告されるようになる。

.*com\.example\.myapp\.foo\.Foo.*

ただこの方法では、無視された違反を反復的に改善していくといったアプローチが取りづらい。改善の都度、対象となる違反が検出されるように正規表現を書き換える必要があるからだ。

そこで、もうひとつの手段である FreezingArchRule を使う。FreezingArchRule は、報告された違反を ViolationStore に保存する。これによって、新たな違反のみを報告し、既知の違反は無視するようになる。そして、修正された違反は、ViolationStore から削除される。

FreezingArchRule の良いところは、ViolationStore での保存先としてテキストファイルを使用する点だ。このファイルをバージョン管理すれば、レガシーシステムの継続的な改善を追跡できるようになる。

使い方は次のように、対象とする ArchRuleFreezingArchRule でラップするだけだ。

ArchRule rule = FreezingArchRule.freeze(classes().should()..省略..);

ソフトウェアプロダクトを漸進的に成長させていけば、変化への適応や、学習による改善によって、ソフトウェアプロダクトの構造を変化させるべくルールを書き換えることもある。そのようなケースでも、FreezingArchRule が役立つだろう。書き換えたルールが大きな変更であれば、プロダクションコードを一気に書き換えることはできず、時間を書けて反復的に改善することになるからだ。これは、技術的負債の四象限の右下にあたる「慎重で無自覚な負債」だと言える。

アーキテクチャとは「変化させられないもの、変化させにくいもの」とされてきたが、この右下の象限が示すように、変化の必要性は生じる。進化的設計(evolutionary design)進化的アーキテクチャ(evolutionary architecture)には、これが背景にあるのだろう。繰り返し変更を加えるソフトウェアプロダクトに関わっていれば経験的に気付くように、アーキテクチャが変化しないことを前提にしてしまうと、そのソフトウェアプロダクトの変更容易性は絶対的にも相対的にも低下していくことになる。このような事態を能動的・積極的に回避する用途としても、ArchUnitの活用を検討したいところだ。

アーキテクチャメトリクスの計測

静的コード解析ツールで計測するコード品質に関するメトリクスと同様に、アーキテクチャにもメトリクスがある。例えば、書籍『Clean Architecture』では、著者ロバート・マーチン(Robert C. Martin)による次のメトリクスが紹介されている。

メトリクス 説明
ファン・イン(fan-in) 依存入力数。コンポーネント内のクラスに依存している外部のコンポーネントの数
ファン・アウト(fan-out) 依存出力数。コンポーネント内にある、外部のコンポーネントに依存しているクラスの数
I(Instability) 不安定さ。 I = FanOut \div \left(FanIn + FanOut\right)
A(Abstractness) 抽象度。 Naコンポーネント内の抽象クラスとインタフェースの総数、 Ncコンポーネント内のクラスの総数とした場合、 A = Na \div Nc
D(Distance) 距離。 D = | A + I - 1 |

ArchUnitでは次のように、ComponentDependencyMetrics  を使って簡単に計測できる。ここで、afferent couplingはファン・イン、efferent couplingはファン・アウトの旧名称だ。

Set<JavaPackage> packages = classes.getPackage("com.example.myapp").getSubpackages();
MetricsComponents<JavaClass> components = MetricsComponents.fromPackages(packages);
ComponentDependencyMetrics metrics = ArchitectureMetrics.componentDependencyMetrics(components);

int fanIn = metrics.getAfferentCoupling("com.example.myapp.component");
int fanOut = metrics.getEfferentCoupling("com.example.myapp.component");
double i = metrics.getInstability("com.example.myapp.component");
double a = metrics.getAbstractness("com.example.myapp.component");
double d = metrics.getNormalizedDistanceFromMainSequence("com.example.myapp.component");

その他にどのようなアーキテクチャメトリクスがArchUnitでサポートされているかは、ArchitectureMetrics クラスから辿ることができる。

テストの実行パフォーマンス

少し気になるのは、テスト対象とするクラスのインポートに関するパフォーマンスだ。これについてはまだ詳しくは調べていないが、インポートされたクラスのロケーションごとにキャッシュされるようだ。同一のURLやJarからインポートしたクラスであれば、テストクラス間で再利用されるということだろう。

ちなみにこれはデフォルトの挙動で、次のように CacheMode.PER_CLASS を指定してやることで、テストクラス単位でのキャッシュに切り替えることもできる。

@AnalyzeClasses(packages = "com.example.myapp", cacheMode = CacheMode.PER_CLASS)
public class FooTest {
    ...

いずれにしても、ArchUnitによるアーキテクチャの本格的な自動テストは、多少の実行時間を要しそうだ。ビルドパイプライン上では、通常のユニットテストやスモールテストより後ろのステージに配置すべきかもしれない。

継続的な変更容易性の獲得による進化

ArchUnitによって、ソフトウェアアーキテクチャとしてデザインされた構造は「テスト容易性(testability)」を手に入れた。そのインパクトは大きい。その恩恵は、開発者が書いたコードを検証することだけにとどまらない。

テストとはすなわち、フィードバックを得ることでもある。ArchUnitで書いたテストコードは、ソフトウェアプロダクトの構造に対するフィードバックループを形成し、そのサイクルを高速化するはずだ。TDDを思い起こすと分かるように、テストコードによる短期間のフィードバックは、実装だけでなく、設計自体の変更も促す。TDDほど短期間でなくとも、少なくともこのプロセスはテストファーストだと言える。構造に関するテストコードの存在は、構造設計そのものの変更も促すのではないだろうか。そしてそれが、「変化させられないもの、変化させにくいもの」とされてきたソフトウェアアーキテクチャに変更容易性をもたらす。

テキストや図でドキュメント化された開発ガイドラインをこまめにアップデートする組織はあまり聞かないが、コード化されたルールならどうだろうか。テストで検出されず、コードレビューで頻繁にコメントされるような指摘事項は、すぐにテストコードとして書いてしまえば良い。逆に、テストによって違反として検出されることが多いルールが、実は現状にそくしたものではなく、変更容易性を悪化させているのだと気付くこともあるだろう。このようにして追加、変更されたルールは、即座にテストスイートとして機能する。そしてまたフィードバックを得る。ドキュメントベースのガイドラインでは、そうはいかなかった。変更箇所を開発者が読んで理解しない限り、機能しないからだ。

テスト容易性を得たアーキテクチャはもはや、静的なものではなくなる。フィードバックループの中でアーキテクトと開発チームが絶えず学習し、その成果としてルールが見直されることで、継続的に変更容易性を獲得し続ける。これこそが、「進化的(evolutionaly)」あるいは「進化可能性、進化容易性(evolvability)」と呼べるものではないだろうか。

エンジニアリングマネージャーという役割にどう向き合うか

エンジニアリングマネージャーとしての日々の仕事にやりがいを感じられない。そういう人は多いのではないか。

「マネジメント業務の中では達成感を得られず、自身の成長も感じられない」「会議や調整ばかりの毎日で、週末に一週間を振り返ってみても、明確なアウトプットもなく、自分が一体何をしていたのか思い出せない」といった声をきく。

やりがいを感じられないのは、やったことに対する明確な手応えを感じられないからだろう。エンジニアとしてソフトウェア開発に邁進していた頃とは違い、マネージャーとはそういうものだと半ば諦めてしまっているのかもしれないが、その認識は正しくない。ソフトウェアエンジニアがソフトウェアを開発するように、エンジニアリングマネジャーは、エンジニアリング組織を開発している。マネジメントをそう捉えると、様々な見え方が変わる。手応えも感じられるようになる。

手応えとは、言い換えれば「フィードバック」だ。ソフトウェアデリバリでは変更に対してフィードバックを得ることで学び、プロセスを素早く適応させる。このフィードバックループによって、ソフトウェアの価値を高め続ける。マネジメントにおいて手応えを感じられないのだとすれば、それは、エンジニアリング組織の価値を高めるための「変更」に取り組めていないのかもしれない。日々の業務に忙殺され、何をすべきか思考する時間を奪われてしまっているのではないか。

高めるべきエンジニアリング組織の価値とは何だろうか。それは、優れたソフトウェアを作り出す能力だと私は考える。そのためにはもちろんエンジニア一人ひとりの技術力向上も必要であるが、適切な組織構造やプロセスも必要となる。そして、コンウェイの法則によって組織構造に強く影響を受けるアーキテクチャについても考えなければならない。

それこそ、バックログを作り、優先順位を付けて、エンジニアリング組織の変更に取り組むのも良いだろう。その変更が成功したかどうかは、あらかじめ決めておいたメトリクスによって判断する。意図した通りにメトリクスの値が変化したならば、最高の達成感を得られるだろう。そうならなくても学びを得られる。もちろん全てを定量化できるわけではないので、そういったケースでは組織内外に変更結果についてヒアリングすれば良い。

エンジニアリング組織のマネジメントとは、まさにエンジニアリング組織に対するエンジニアリングだ。そういった観点から、エンジニアはそもそもマネージャーに向いているのではないだろうか

日々の業務に追われ過ぎると、組織について考える時間を取れない。組織改善に向けたバックログにアイテムを追加することすらできない状態に陥る。マネージャーには考える時間と、考えたことを実行し、その結果を評価する時間が要る。

エンジニアリングマネージャーとしての日々の仕事にやりがいを感じられないのなら、忙殺される毎日から抜け出すことが、まず最初にやるべきことになるだろう。エンジニアリングマネージャーとして、エンジニアリング組織の改革・改善に没頭できたなら、こんなにやりがいのある仕事はないと感じられると思う。

mtx2s.hatenablog.com

mtx2s.hatenablog.com

エンジニアリングマネージャーとしてのミッション

ソフトウェアプロダクト開発領域を預かるエンジニアリングマネージャーとして、あなたのミッションは何であるか。そう問われれば迷わず、組織としての「プロダクト開発能力の差異化」だと答える。これはもちろん私個人の見解ではあるが、受託開発組織のマネジメントを離れ、プロダクト開発組織を主としてマネジメントするようになった10年以上前から変わらない。

「プロダクトの差異化ではなく?」と聞き返されることも多い。ユーザーやビジネスにとって価値ある優れたプロダクトやフィーチャを作り出すことはもちろん第一級のミッションだ。そうであっても、そこで得られた成功が "偶然" であるなら組織としての持続性がない。「プロダクト開発能力の差異化」とは、そういった成功に再現性を持たせることを意味する。

組織としての「プロダクト開発能力の差異化」

そもそも優れたプロダクトやフィーチャは他社に真似されやすい。先発優位が長続きする市場はその数を減らしつつある。競争優位はもはや一時的で、持続しないことを前提に考えるべきだろう。経営学者のリタ・マグレイス(Rita McGrath)は、一時的な競争優位を同時並行的に確立し続けることが、長期間にわたるリードに繋がると説いた。優れたプロダクトやフィーチャを作り出して練り上げるだけでなく、更に新たな価値を生み出す。何度も何度も創造する。ここに、成功への再現性が要る。

この「再現性」というものは、組織が経験を積み重ね続けて得た能力(スキル)だ。それは、組織の存続年数に比例するものではない。ソフトウェアデリバリという実務の遂行に追われているだけでは備わらない。組織レベルでの経験学習サイクルをもって備わるものだ。

カイゼン」を文化にまで落とし込んだトヨタに代表されるように、学習が文化として定着した組織は強い。この観点では、学習する組織を作り上げることがミッションであるとも言える。

優れたプロダクトやフィーチャは真似されやすくても、こうして組織に備わった能力というものは、そう簡単に真似されるものではない。

開発能力はデリバリパフォーマンスとして表れる

では、肝心な「組織としてのプロダクト開発能力」とは何であるか。ソフトウェアエンジニアリングを担う組織にとってのそれは、ソフトウェアデリバリのパフォーマンスだ。ニコール・フォースグレン(Nicole Forsgren)らの調査によって、ソフトウェアデリバリのパフォーマンスは、組織全体のパフォーマンスに影響する予測要因であり、両者は正の相関関係にあることが明らかにされた。ソフトウェアエンジニアがそれまで経験的に感じていたことが、正しかったと調査によって示されたのだ。

ソフトウェアデリバリのパフォーマンスは、4つのメトリクスで表される。デプロイの頻度(deployment frequency)変更のリードタイム(lead time for changes)平均修復時間(time to restore service)変更失敗率(change failure rate)だ。この4つのキーメトリクスこそ、開発チームが経験を積み重ねて獲得する能力を観測するものだ。

Accelerate State of DevOps Reportでは、調査結果に基づいてこれらのメトリクスをEliteパフォーマンスからLowパフォーマンスまで4段階に分類している。自チームのスコアと比較することで、優位性を把握することが可能となる。もちろん、開発チームはそれぞれ置かれた状況や事情が異なる。必ずしもこのパフォーマンス分類がそのまま当てはまるわけではないが、参考にはなるはずだ。

ソフトウェアデリバリのパフォーマンス(2021年版)

組織構造とプロセス、アーキテクチャを進化させ続ける

組織としての「プロダクト開発能力の差異化」を実現し、高めていくためには、組織改革は継続的なものとなる。終わりがなく、永遠に完成することはない。進化していくように適応を繰り返す。

そこでの改革対象は、組織構造プロセス、そしてアーキテクチャの3つを常にセットで考える。取り組む順番は組織構造、プロセス、アーキテクチャの順が良い。組織構造およびプロセスが、アーキテクチャに影響を与えるからだ。これはコンウェイの法則としておなじみだろう。

また、アルフレッド・チャンドラー(Alfred Chandler)の「組織は戦略に従う」という言葉のように、戦略に合わせて組織設計から始めることが自然にも感じる。組織設計にはコンウェイ戦略を用い、その後に控えるアーキテクチャの変更を連動させる。

組織は戦略に従い、システムは組織に従う」とでも言えば良いだろうか。ビジネスを取り巻く内外の状況変化に応じて戦略は変化する。その変化に対し、組織を柔軟に変化させ、アーキテクチャを進化させ続けるということだ。

組織構造でコミュニケーションコストを最小化する

組織をどのようにチーム分割するか。その結果が組織構造となる。チームを組織にとってのコンポーネントだと捉えてみると、設計の方向性が見えてくる。

ソフトウェアコンポーネントと同様に、組織コンポーネントたるチームも高凝集であるべきで、かつ、チームは互いに疎結合であるべきだ。そうすれば、それぞれのチームのソフトウェアデリバリは互いに干渉することがない。ソフトウェアの変更もデプロイも、他チームや誰かの助けや承認を得ること無く、チームが独立して実行可能になる。

これについては以前にも『開発組織を分散モノリスにしないチーム分割と協働のデザイン - mtx2s’s blog』というタイトルでブログ記事を書いた。

mtx2s.hatenablog.com

高凝集で疎結合な組織は、チーム内でのコミュニケーションが密(高帯域幅)に、チーム間でのコミュニケーションが疎(低~中帯域幅)になる。これは、組織内のコミュニケーションコストを最小化するとともに、このコミュニケーション構造がソフトウェア設計にも良い影響をもたらす。

チームトポロジー』の著者であるマシュー・スケルトン(Matthew Skelton)らに言わせれば、「多くの組織はいつでもコミュニケーションは多いほうがよいと考えるが、実際にはそうではない」ということだ。『アジャイルな見積りと計画づくり』の著者としても知られるマイク・コーン(Mike Cohn)も、ブログ記事 "Nine Questions to Assess Team Structure" にて、「チーム間のコミュニケーションパスの数を最小化する構造になっているか?」と問うている。

組織構造を設計するにあたっては、エリック・エバンス(Eric Evans)のドメイン駆動設計(DDD, Domain-Driven Design)が役立つ。ソフトウェアプロダクトが扱う対象領域を適切にコンテキストに分ける。そしてこのコンテキストをもとに、どのようにチーム分割するかと、それぞれの責務が形作られる。その結果は、戦略に影響を受けるはずだ。こうして作られたチームの多くはストリームアラインドチーム(stream-aligned team)となり、それぞれが独自のバリューストリームに組み込まれることになるだろう。

プロセスにフィードバックループを多重に織り込む

ソフトウェアデリバリのサイクルを重ねる度にチームが生み出す成果と言えば、プロダクトバックログアイテムを実現したインクリメント(フィーチャや機能)をまっさきに思い浮かべる。チームが成果をそのように捉えているならば、そのチームは優れたプロダクト開発能力を得ることはできないだろう。

チームがコミットすべきはその先、つまり、インクリメントのリリースによって、優れたユーザー体験を生み出すことだ。それがユーザー価値となり、ビジネス価値につながっていく。

mtx2s.hatenablog.com

このような優れたユーザー体験を生み出す開発能力は、どのようにして備わるのだろうか。

優れたチームは、デリバリサイクルを重ねる過程で、プロダクトナレッジプロジェクトナレッジという、チームの開発能力に対するインクリメントを作り出す。プロダクトナレッジは、「何を(what)作れば良いか」という目標不確実性を低減し、チームを正しいプロダクト価値に近づけていくためのナビゲーション精度を高める。プロジェクトナレッジは、「どう(how)作れば良いか」という方法不確実性を低減し、ソフトウェアデリバリにおける様々なプロセスを洗練させる。

経験に基づいて高濃度で高品質なナレッジを抽出するには、フィードバックの獲得は欠かせない。フィードバックループのサイクルが短いほど、その純度は高まる。アジャイル開発手法DevOpsリーン開発は、プロセスにフィードバックループが組み込まれている点が優れている。

スプリントレビュー」は、数日から数週間単位でのフィードバックだ。デプロイ頻度や変更のリードタイムを改善していく目的のひとつがこのフィードバックループの高速化だと考えると良い。「テスト駆動開発」は数十秒から数分単位のフィードバックループだ。「継続的インテグレーション」や、「ユーザーフィードバックの収集と活用」もフィードバックループだ。

上図は、VersionOneのポスター(原典は既に存在しない)をもとに書き起こしたものだ。このように、大小さまざまなフィードバックループを幾重にもプロセスに織り込む。それが、ソフトウェアデリバリのパフォーマンスを大きく左右するほどのナレッジの継続的な獲得につながっていく。

アーキテクチャによってデプロイとテストの容易性を高める

ここで言うアーキテクチャとは、ソフトウェアがどのようにコンポーネント分割されているかと、それらの依存関係を指す。コンポーネントとは、『Clean Architecture』で言うところの「デプロイの単位」であり、「システムの一部としてデプロイできる、最小のまとまり」を指す。

アーキテクチャで注力すべきは、コンポーネントデプロイとテストの容易性だ。フォースグレンらは、著書『LeanとDevOpsの科学』の中で、デプロイとテストの容易性について次のように定義している(定義内の「アプリケーション」を「コンポーネント」として読み替えて欲しい)。

  • テストの大半を、統合環境を必要とせずに実施できる
  • アプリケーションを、それが依存する他のアプリケーションやサービスからは独立した形で、デプロイまたはリリースできる(そして実際にもデプロイまたはリリースしている)

この定義に更に、チームの凝集性に関する3つめの要素を加えたい。

これら3つに当てはまるならば、チームは高い独立性を持ってソフトウェアデリバリを遂行できる。逆に言えば、この定義に当てはまらないチームは、独立性が低く、ひとつのプロダクトバックログの実現のために、他のチームの力を借りなければならない事態が頻発するということだ。これではパフォーマンスを発揮できるはずがない。

忘れてはならないのは、組織構造やプロセスの変化がバリューストリームの流れを変え、それが各チームのプロダクトバックログの内容を特徴づけるということだ。つまり、コンテキスト境界に変化が起きているのだ。組織構造、プロセス、アーキテクチャをセットで変えていく必要性は、ここにある。

しかし、経験から言って、組織改革を進める上でアーキテクチャの変更が議題に挙がることはまれだ。組織構造プロセスをどう変えるか。集中的に議論されるのはそればかりだ。

それはそうだろう。組織構改革にアーキテクチャとの関係性を見出すことは難しい。当たり前のように、アーキテクチャだけが取り残されてしまう。改革前の組織によって形作られたアーキテクチャが、新しい組織と上手く噛み合うとは限らない。

新しい組織構造やプロセスにあわせ、アーキテクチャが理想的な形へと自然に変化することなど期待できない。新しいアーキテクチャを仮説として持ち、それをフィードバックループの中で漸進的に進化させながら形にしていく。その仮説は、ドメイン駆動設計を詳細化する中で、組織構造とプロセスにも反映されている。これこそが、コンウェイ戦略だろう。

保守性を高める技術を学ぶ

ソフトウェアプロダクト開発というものは、繰り返し繰り返しソフトウェアを変更する活動だ。この「変更する」という活動を中心にソフトウェアデリバリを見つめ直すと、「変更しやすさ」がデリバリパフォーマンスのキーファクターのひとつであると気付く。変更しにくいソフトウェや変更できなくなったソフトウェアは価値を失い、競争力を失う。プロダクトをそのような状況に追い込んだ組織に、優れたプロダクト開発能力があろうはずがない。

mtx2s.hatenablog.com

変更しやすさ、すなわち変更容易性(modifiability)は、理解容易性(understandability)テスト容易性(testability)と並んで保守性(maintainability)を構成する。バリー・ベーム(Barry Boehm)らの定義するこの3つの品質特性は、互いに関連しあっている。理解容易性を高めれば、変更容易性も高まる。テスト容易性が高いコードは、理解容易性も変更容易性も高いと言われる。

コードの保守性を高めるためには、リファクタリングが欠かせない。リファクタリングと言えば、いわゆる「レガシーコード」と呼ばれる既存コードに対する技術的負債を返却する行為というイメージがあるが、実際には新しいコードを書くにも必要な技術だ。それは、ケント・ベック(Kent Beck)エクストリーム・プログラミングと共に広めたテスト駆動開発リファクタリングが組み込まれていることからも分かる。

ここで私は、リファクタリングを「技術」と言った。マーティン・ファウラー(Martin Fowler)の著書『リファクタリング』や、マイケル・フェザーズ(Michael Feathers)の著書『レガシーコード改善ガイド』などからも分かるように、リファクタリングはスキルなのだ。そしてリファクタリングの実践にはテストコードが付きものであり、その手法たるテスト駆動開発もまたスキルだ。保守性を高めるためには、この2つのスキルを伸ばす必要がある。

当然ながら、テスト駆動開発リファクタリングの技術的な基礎となるのは、オブジェクト指向といったプログラミングパラダイムへの深い洞察と理解だ。そしてそこから優れたアーキテクチャが生まれる。

これらのスキルや知識は、必要に迫られてのコードリーディングや、ペアプロなどによるスキルトランスファーといった、OJTでも獲得できる。しかしここにOFF JTを加えることで、その効果はより高められる。良い意味で意識の高いチームは、技術書の読書会を定期的に開いたり、技術研修に参加したり、LT大会でチーム内外の知識の共有を促進している。

ソフトウェア開発組織は、日々のソフトウェアデリバリ業務に忙殺されがちだ。実務外のこのような取り組みはなかなか導入しづらいだろう。しかし、だからこそスキル向上に力を入れられたならば、「差異化」につながるのだ。

組織を動かすのは人である

「組織は戦略に従う」とは言ったものの、組織を動かすのは人である。彼らは、戦略やミッションに理解と共感を示し、誇りを持って業務を遂行できているだろうか。こういった従業員エンゲージメントは、その高さがパフォーマンスに影響を及ぼす。

従業員エンゲージメントはeNPSで定点観測可能であるが、そのスコアの上下が何に起因しているか正確に分析することは難しい。その深層を1on1を通して汲み上げようと試みるも、対策可能なほどに具体的な問題にまで焦点を絞り込むことがなかなかできない。そもそもeNPSの被観測者本人でさえ明確には理由が答えられないからだ。

マネージャーがコーチングスキルを高めていけば解決しそうではあるが、これは結局、1on1やコーチングをマネージャーによる情報収集の手段としている点で適切なアプローチとは言い難い。そもそも、マネージャーが問題を把握し、施策を展開するより、チームで話し合って問題解決に取り組む方が実情に即したものになりやすく、達成感も得られやすいのではないだろうか。それこそが自律的な組織ではないか。マネージャーとして、そこでチームから出てくる相談に協力は惜しまない。

ジェフ・サザーランド(Jeff Sutherland)が、チームによる「幸福度の計測」と呼ぶ手法を著書『スクラム』で紹介している。チームがスプリントレトロスペクティブの度に「どうしたらより幸せになれるか、満足できるか」を問う3つの質問にメンバー全員が答え、チーム全体で改善に取り組むというものだ。これを続けて幸福度を上げていくことでベロシティが3倍になったと言う。

また彼は、チームのメンバーを実際に幸せにする要素とは、主体性スキルアップ目的意識だと断言している。さらにこれらをそれぞれ「自分の運命を自分でコントロールできること」「何かについて自分が上達しているという実感」「自分より大きな何かに力を尽くしているという感覚」と言い換えている。エンジニア経験があれば、この言葉に共感できるのではないだろうか。これらの価値を尊重し、チームが自己組織化していく文化の醸成にこそ、マネージャーは力を尽くしたい。

戦略やミッションへの理解と共感は、目的意識を醸成するだろう。それが、組織構造とプロセスの本質を捉えた行動や判断につながる。実践に基づくプロダクトナレッジとプロジェクトナレッジの獲得や、学びの文化は、スキルアップとなってデリバリパフォーマンスを向上させる。チームによる幸福度の計測は、主体性を高め、チームを自己組織化させる。

成功への再現性」という点では、人材育成や組織内の流動性も欠かせない。人の価値観は変わりくいからだ。長期間、顔ぶれが変わらず役割が固定化した組織は、価値観が固定して新しいものを生み出しにくくなる。属人化も起きるだろう。だからこそ新しい世代の台頭や、新たな文化の流入が必要だ。このサイクルには、我々のようなマネージャーも含まれている。

こうして絶え間ない進化を続ける組織こそ、「プロダクト開発能力の差異化」を手にする組織なのだ。

mtx2s.hatenablog.com