mtx2s’s blog

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

組織設計の失敗に見るソフトウェア開発への悪影響

問題を抱えるソフトウェア開発組織を観察すると、その根本原因が組織設計にあると気付くことも多い。組織の構造的な歪みがまるで力場を形成するかのように、そこに配置された様々な要素に作用し、悪影響を及ぼしている。

組織設計のまずさから生じる問題というものはとらえ難く、理解し難い。その問題に気付かないから、真っ先に矛先がソフトウェアエンジニアリング領域へ向いてしまい、開発プロセスやソフトウェアアーキテクチャの改善を繰り返すが大きな効果が得られない。効果があったとしてもそれは一時的で、気付けば元に戻っている。

コンウェイの法則を思い出せばその従属関係に頷けるはずだが、多くの現場ではこういったソフトウェアエンジニアリング領域に閉じた施策に終始しがちではないだろうか。

以下に挙げる3タイプの組織問題は、ソフトウェアエンジニアやマネージャーとして私が実際に経験したいくつかの事例をタイプ別に集約したストーリーだ。複数事例の問題点を凝縮したため極端なストーリーになってしまった感は否めないが、その方が問題が強調されて理解しやすい。ここには問題の解決策が書かれていないが、失敗を回避するヒントや気付きを得るぐらいの価値はあると期待している。

プロジェクトチームとリソースプール

プロジェクトチームと呼ばれる体制は、どうやら多くのケースで"直感的に"採用される選択肢のようだ。プロジェクトの性質に適した人材をアサインできる柔軟さを持ち、それ故にクロスファンクショナルであり、スピード感がある、と。少なくともこの体制を選択する人たちは、そう信じているようだ。

これが全くの間違いだとは思わないが、最も基本的な制約と前提が考慮されていない。人材は有限であることと、いつだって多くのプロジェクトが並走しているということだ。

これらの事実から、プロジェクトチームがいわゆる「ドリームチーム」になることなど奇跡だ。プロジェクトマネージャーは、何とか頭数だけ揃えた体制でプロジェクトをスタートせざるを得ない。これで上手くやっていけるのか不安になる。やはりチームには、優秀なエンジニアにも参加して欲しい。

優秀なエンジニアは年中引っ張りだこだ。同時にいくつものプロジェクトを掛け持ちしている。そんな彼らから稼働時間を搾り取っても、せいぜい10%や20%程度が良いところだろう。その短い時間を使うことでさえ、彼らのスケジュールに合わせなきゃいけない。これがクリティカルチェーンとなって、プロジェクトのリードタイムを引き伸ばす。

プロジェクトを掛け持ちしているのは、なにも優秀なエンジニアだけじゃない。人員数に対し、プロジェクトの並列数が多すぎるのだ。誰もがプロジェクトを掛け持ちしている。掛け持ちするプロジェクトの数が多いほど、出席しなければならない会議体も多くなる。エンジニアはもはやコードを書く時間などほとんど残されていない。これでタスクを遅延させるなと言う方が無茶だ。何とか残業でこなすしかない。

残業で長時間労働が常態化すると、新しい技術を試したり学んだりする時間をエンジニアから奪う。実務の中で伸ばせるスキルもあるが、ソフトウェアエンジニアリングの世界で生き抜くにはそれだけで十分なのだろうか。技術も方法論も日進月歩だ。このままでは個人としても組織としても世界に遅れをとる。その上、プロジェクトチームはプロジェクトが終われば解散する。チームとして積み上げた能力もそこで失われるのだ。唯一の救いは、プロジェクトで得たナレッジをライン組織内で共有していることだけかもしれない。

エンジニアが所属するライン組織は、さながらリソースプールだ。プロジェクトが立ち上がる度にエンジニアを貸し出す。ラインマネージャーは、誰がいつからいつまでどのプロジェクトにアサインされるのかをスケジューリングし、管理する存在でしかない。どこかのプロジェクトで遅延が発生すると、プロジェクトから人員が解放される時期も遅れる。それがリソース計画を狂わせ、他の複数のプロジェクトに影響を及ぼす。マネージャーは、プロジェクト間でのリソース調整に常に追われることが宿命のようだ。

このような中でラインマネージャーはどうやってメンバーを評価するのだろう。メンバーは、日常的な業務をプロジェクト内で遂行している。その活躍ぶりをラインマネージャーが観察する機会は多くない。評価に対するメンバーの納得度は限りなく低くなるだろう。

モノリスチームという技術的負債生成器官

はじめは少人数のチームであっても、マーケットにおけるプロダクトの存在感が高まるにつれ増員を重ねることになる。そこからの道は二手に別れているが、その分岐にさえ気付かず、チームもソフトウェアシステムもモノリス化へと突き進んでしまう。そんなソフトウェアプロダクト開発組織もある。むしろ昔はその方が主流だったように思う。

このようなモノリスチームは、20~30名を優に超える大所帯を抱えた単一チームとしてソフトウェアデリバリ業務にあたる。

モノリスチームが育てたモノリスシステムは巨大過ぎて、我慢ならないぐらいビルドが遅い。こまめに統合していたら時間がいくらあっても足りなくなるから統合の頻度が下がる。継続的インテグレーションにはほど遠い。こんな有りさまだから、いざ統合しようという段階で問題が頻発する。

その上、バッチサイズは常に大きい。そりゃそうだ。チームサイズが大きいのだから、それに比例してバッチサイズも当たり前に大きくなる。そしてそれは、リリースリスクが大きくなることも意味する。リリースする度にトラブルを起こすので、CFR(Change Failure Rate)は酷いあり様だ。

チームにとってリリースは恐怖でしかない。そのストレスから逃れたいから、リリース頻度は下がる一方だ。残念なことにそれがバッチサイズをまた一段と大きくし、リリース時のトラブルは益々頻発することになる。

多発するトラブルを一刻も早く収めるよう、経営リーダーからプレッシャーを受けるのはマネージャーだ。もはやチームに任せてはいられない、自分が何とかせねばとマイクロマネジメントが常態化する。品質を上げたい一心で、マニュアルテストはより重厚になる。あらゆる開発フェーズには長い長いチェックリストが作られていく。それらが正しく実施されたことをチェックする承認フローまで設けられる。プロセスはより厳格で重厚なウォーターフォールになっていく。

かろうじて品質が安定しだしても、悲しいことに経営リーダーは喜ばない。リードタイムが長くなり過ぎたのだ。こんなパフォーマンスでは、ビジネスチャンスを掴めない。競合プロダクトに先を越され、マーケットでの競争力など無いに等しい。

その一方で、数多くのテストケースとチェックリスト項目に埋もれたチームメンバーたちは、自分がまるでマシーンになったように感じはじめる。指示されたことにただ従うだけの存在だ。規律正しいことだけが評価される。達成感も得られず、仕事は楽しくない。心血を注いできたプロダクトの競争力は無くなり、誇りも持てなくなった。そうして一人、また一人と会社を去っていく。優秀な人ほど先に辞めていく。

チームは欠員を補充しながら何とかこの鈍重なプロセスをまわし続けるが、初期からのチームメンバーが去った傷跡は大きい。システムがブラックボックス化してしまうからだ。小さな変更でさえ、その影響範囲の調査に膨大な時間がかかるようになる。詳細設計レベルまで進めないと開発規模を見積もることさえできない。だがそこまでしても、影響範囲の考慮漏れによるトラブルを度々発生させてしまう。

影響範囲を最小限にとどめたいから、消極的な設計が横行するようになる。既存コードや既存データにできる限り影響を及ぼさない設計を選択してしまうのだ。本来であれば既存のメソッドの変更で済むようなケースであっても、もとのメソッドを複製してそちらを変更する。こうしてコードの重複が生み出され続け、保守性は著しく悪化していく。

こんなことが巨大なバッチサイズでリリースの度に繰り返されるのだから、状況は酷くなり続ける。モノリスチームはまるで、技術的負債の生成器官のようだ。このネガティブスパイラルはいずれ、プロダクトを変更不可能な状態にまで追いやるだろう。

かろうじてチームに留まってくれた古参で優秀なエンジニアは、チーム内外から引っ張りだこだ。彼に聞けば安心だ、彼を通さないなら進めちゃだめだ、となる。そうやって属人化が進み、彼がボトルネックとなってリードタイムは更に悪化することになる。

低凝集チームが構成する分散モノリス組織

モノリスチームと対極にあるスモールチームならどうだろうか。7±2名程度のメンバーが固定で所属するチームが複数集まり、ソフトウェアプロダクトをマイクロサービスアーキテクチャやモジュラモノリスで分割統治する。このようなタイプの組織は今や珍しい存在ではないが、必ずしも上手くいくとは限らない。

複数チームと言っても人材には限りがあるから、必要な数だけチームを配置することは現実的に難しい。無理にチーム数を増やすと、一部のメンバーはチームを掛け持ちせざるを得なくなる。チームを掛け持ちするメンバーがいると、チーム間でのリソース管理が必要になる。掛け持ちメンバーによってチーム間が結合されているのだ。この調整によって、それぞれのチームのリードタイムが引き伸ばされてしまう。

一方でソフトウェアプロダクトのコンポーネント間は、依存関係を上手く設計したので結合度は低い状態だ。コンポーネントにはそれぞれオーナーとなるチームを決めており、コンポーネントへの変更はオーナーチームが担当する。どのコンポーネントでも自由に変更できるよう、オーナーを決めないやり方もあるが、そうするとコンポーネントへの変更でチーム間調整が頻発する恐れがある。複数チームで同一コンポーネントを同時期に変更することが起こり得るからだ。

これでチームが独立してコンポーネントを変更し、デプロイできるはずだった。しかし現実はそう甘くない。ひとつのフィーチャ開発で変更対象となるコンポーネントの数は、複数に及ぶことの方が多い。そのオーナーが単一のチームに収まらない。コンポーネントの変更に関してチーム間で調整する必要が頻発したのだ。

これは、各チームに対するコンポーネントオーナ権のパッケージングが適切ではないことによるものだ。チームとしての凝集度が低い、と考えれば分かりやすい。DDD的に表現すれば、コンテキスト境界が適切ではないということだ。

諦めて複数チームで協力して開発を進めることにするが、このやり方はチーム間にタスクの従属関係を生じさせる。一般的に、リードタイムを悪化させる主な原因としてタスクの従属関係が挙げられる。そのタスクの従属関係がチーム間で発生するのだから、リードタイムへの影響はなおさら酷い。あるチームにとっては優先順位の高いフィーチャ開発が、別のチームにとってもそうであるとは限らないからだ。チームはそれぞれにミッションや目標を持つため、チーム間で優先順位を揃えることは容易くない。

何とかスケジュールの調整を終えて開発を進めても、チーム間での開発スケジュールの合流ポイントで問題が起きやすい。従属関係にある先行タスクを担うチームの進捗に遅延が生じれば、後続タスクを担うチームにも遅れが連鎖する。ようやく両チームの成果物を結合すると、今度は両者の仕様の解釈に齟齬があることが発覚する。そしてその手戻りで更に時間を奪われる。

このような調整や齟齬が頻発するから、チーム合同でのミーティングが増えていく。2チーム程度であればそのコストも大したことはないが、コミュニケーションコストはチーム数の自乗のオーダーで大きくなる。チームが多いほどそのコストは組織に重く乗しかかり、ソフトウェアデリバリのパフォーマンスを削り取っていく。

「分散モノリス」という呼び名は、本来、マイクロサービスアーキテクチャアンチパターンを指すものだ。この名を組織タイプのラベルとして用いたのは、問題構造がよく似ているからだ。チームの凝集度が低いために、チーム間の結合度が高くなる。これが、分散モノリス組織の正体だ。

失敗から学ぶ

多くのマネージャーは、自ら組織を立ち上げるより既存の組織を引き継ぐことの方が多い。そうして担当することになった組織のパフォーマンスを全体最適の中で最大限に高めることがミッションだ。

その遂行には組織のリファクタリングが欠かせないが、いくつもの問題が絡み合って解決の糸口が見いだせないこともある。それこそが、本記事の3つのストーリーのように、組織の構造的な歪みによって生じる問題の可能性が高い。時には大胆に組織をリアーキテクティングすることも必要だ。

最後に、本記事で取り上げた問題のいくつかを列挙する。組織設計の深刻な失敗を回避するヒントや気付きになることを期待して。

  • 優れたプロジェクトチームが編成されることは稀だが、時間をかけてチームが成長してもプロジェクト終了と同時にその能力は失われてしまう
  • プロジェクトの掛け持ちはクリティカルチェーンとなってリードタイムを悪化させる
  • リソースプール化したライン組織では、マネージャーが正しくメンバーを評価することが難しい
  • チームメンバーの増加はバッチサイズを大きくする。それがリリースリスクを高め、トラブルを頻発させる。するとチームがリリース頻度を下げはじめ、バッチサイズが更に大きくなるというネガティブスパイラルに陥る
  • トラブルの頻発が続くとマイクロマネジメントが始まり、メンバーのやる気を奪い取る
  • ソフトウェアプロダクトを分割統治しない体制は、消極的な設計を横行させやすく、技術的負債を生みやすくする
  • 複数チームに分けてソフトウェアプロダクトを分割統治する体制であっても、チームの凝集度が低ければチーム間の結合度が高まり、コミュニケーションコストが高くつく。それがリードタイムを押し下げる

関連記事

参考までに。

プロジェクトチーム体制に関連する記事

note.com

mtx2s.hatenablog.com

モノリスチーム体制に関連する記事

note.com

mtx2s.hatenablog.com

分散モノリス組織に関連する記事

mtx2s.hatenablog.com

mtx2s.hatenablog.com

その他

mtx2s.hatenablog.com

note.com

マルチバリューストリームというアンチパターン

「保守や運用を考慮した開発」とは言うが、それが意図するところは思うより広い。ソフトウェアエンジニアとしてのその理解は、あくまでもソフトウェアエンジニアリングに閉じたものとなってしまいがちだ。

変更しやすいコードや、システムの安定した稼働は、無論、大切だが、バリューストリーム(value stream)という観点が抜け落ちることがある。そのペナルティとしてチーム間が密結合となってコミュニケーションコストが増大し、テスト容易性デプロイ容易性を著しく損なう結果を招いたプロジェクトもある。

私が聞いた過去のあるケースでは、複数のアプリケーション間で一部の機能の共通化を進めたことでこのペナルティを支払った。何が問題だったのか。どうすべきだったのだろうか。

そうなったいきさつ

通化された機能はもともと複数のアプリケーションそれぞれで実装されていた。その機能は、ユーザインタフェースを含め、いずれのアプリケーションもほぼ同じものだった。「ほぼ同じ」なので、わずかな差はある。それがユーザー体験に差を生じさせることが社内で問題視され、互いにユーザインタフェースを揃えることが急務とされていた。

問題視されたのはユーザー体験の差だけではない。ほぼ同じ機能の開発・改善を続けるために、それぞれでエンジニアが稼働するという重複も避けたかった。エンジニアの人数は限られている。競争が苛烈なマーケットで勝ち抜くには、注力すべき領域にエンジニアリングパワーを集中させなければならない。それを削ぐようなムダは少しでも排除する。それが、経営リーダーの意志だった。

こういった背景から方針として打ち出されたのが共通化だった。件の機能を共有コンポーネントとして切り出して各アプリケーションに組み込む、というものだ。

通化という方式は、実行可能で現実的な判断だった。アプリケーションは、それぞれが異なるビジネス向けに、各々専任のチームによって開発が続けられているが、ウェブアプリケーションという点で共通している。同じ会社の中なので、そこで採用されている技術要素も大差はない。また、各アプリケーションチームがそれぞれで開発していたことからも分かるように、技術的な専門性を要する類のものでもない。

こうして、経営リーダーから号令が発せられ、共通化はビジネス、チームを横断して取り組むプロジェクトとなった。

そしてマルチバリューストリームが発生した

完成した共有コンポーネントによって、ユーザインタフェースは統一された。このコンポーネントひとつに変更を加えれば、全てのアプリケーションに反映できる。

一見すると大成功したかに見えるプロジェクトではあるが、新たに大きな問題を抱えることとなった。共有コンポーネントに対して日々発生する追加開発を通し、チームが互いに密結合になったのである。当然、リードタイムは著しく悪化した。

理由は明らかだった。共有コンポーネントチームが、アプリケーションごとに流れるバリューストリームそれぞれの対応に追われることとなったのだ。「マルチバリューストリーム」とでも言えば良いだろうか。

マルチバリューストリーム
マルチバリューストリーム

アプリケーションチームは、いわゆるストリームアラインドチーム(stream-aligned team)に位置づけされる存在だ。アプリケーションに対するユーザーのニーズや利用目的は、アプリケーションごとに異なる。それぞれにバリューストリームがあり、それを受けて仮説を立て、アプリケーションに変更を加えていく。

ここで問題になったのは、その過程で共有コンポーネントへの変更の必要性が生じることだ。それは即ち、共有コンポーネントに対する変更理由やタイミングが、アプリケーション側によってもたらされることを意味する。この状況が、閉鎖性共通の原則(CCP, the Common Closure Principle)に反することは明らかだ。コンポーネントを変更する理由は、複数あるべきではない。

共有コンポーネントも、それを利用する各アプリケーションも、それぞれが専任のチームによって開発が続けられている。CCPが守られなかったことで、共有コンポーネントに何か変更を加えようとする度に、これらのチームが集まって、仕様や開発、テスト期間、リリース日などについて話し合い、調整しなければならなくなった。

まるで玉突きのようだった。あるアプリケーションへの変更要求の範囲に、共有コンポーネントに対する機能追加・改善が含まれる。すると、共有コンポーネントを変更する影響が、他のアプリケーションにも波及していく。影響は限定的とは言え、チームが互いに密結合になり、共有コンポーネントの存在がビジネスの足かせになっていった。

更に根深い問題は、この状況にあっても、チームが互いに密結合にあることに気付くものがほぼいなかったことだ。むしろ、チーム間の連携をより強くすべきだと、コミュニケーションの回数・時間を増やし出したぐらいだ。共有コンポーネントの変更による影響で、仕様に齟齬の生じたアプリケーションが障害を起こすことが度々発生したからだ。

こうして各所でチーム間のコミュニケーションパスが増えていき、共有や調整を目的とするミーティングが頻繁に開かれ、コミュニケーションコストが組織の処理能力を減衰させる事態に陥っていった。

なにが起こったのだろうか

通化という判断は、間違いだったのだろうか。アーキテクチャの視点で見てみると、依存方向は、各アプリケーションから共有コンポーネントへの一方向だ。共有コンポーネントに対してアプリケーションが従属している。この関係においては、共有コンポーネントに対する変更が外部要因となって、従属アプリケーションを変更することはあり得る。

それだけに、責務を負うこととなった共有コンポーネントに対する変更は慎重にならざるを得ないのであるが、従属アプリケーションに対するバリューストリームが、共有コンポーネントを頻繁に変更するモチベーションになっている。つまり変更理由という視点では、アーキテクチャとは関係性が逆転し、各アプリケーションに対して共有コンポーネントが従属しているということになる。アーキテクチャだけでは読み取れない相互依存関係が形成されているようだ。

従属アプリケーション側の事情による変更
従属アプリケーション側の事情による変更

共有コンポーネントチームから見れば、アプリケーションチームは直接のユーザーだ。アプリケーションチームへの提供価値の拡大は、共有コンポーネントの変更理由となるため、この依存関係は正しく見えなくもない。

しかし本ケースでは、共有コンポーネントチームに対し、各アプリケーションチームからプッシュ型でダイレクトに変更要求が押し寄せるようになっている。それがマルチバリューストリームの正体だ。共有コンポーネントチームは、そうやって多方面から流れ込んでくる多くの変更要求の間で生じるコンフリクトやリリースタイミングの調整に追われ続けていた。

社外サービスやOSSライブラリに対して自社アプリケーションを依存させる場合は、こうならない。自社アプリケーションに対するバリューストリームが、社外サービスやOSSライブラリに流れ込まないからだ。会社の内と外という境界に立ちはだかる高い壁が流れをせき止める。

社内開発する共有コンポーネントにも、組織やチームの内と外という壁はあるが、その高さは社外とのそれに比べてあまりに低い。強引に流れを止めようとしても、社内のコンセンサスを得ることは困難だろう。不特定多数を利用者とするOSSや社外サービスと違い、特定少数を相手にするという点も、社内事情の影響を受けやすい理由だと考えられる。

内外を隔てる壁に頼らず、アーキテクチャをより明確・厳密に分離する必要があるのではないか。それが、共有コンポーネントに流れるマルチバリューストリームを分離し、単一のバリューストリームによる独立したプル型のソフトウェアデリバリを実現する道に繋がるのではないだろうか。

どうやって抜け出そうか

まずは問題の整理が必要だ。共有コンポーネントが範囲とするコンテキスト(bounded context)と、従属アプリケーションから流れ込むバリューストリームが扱う範囲の関係をベン図で描いてみると見えてくるものがあった。

共有コンポーネントのコンテキストとマルチバリューストリームの関係
共有コンポーネントのコンテキストとマルチバリューストリームの関係

共有コンポーネントのコンテキストの中で、いずれのバリューストリームとも重ならない領域(1)が、共有コンポーネント固有の領域と言える。逆に、バリューストリームが共有コンポーネントのコンテキストと重ならない領域(2, 3)は、それぞれのアプリケーションが扱うべき領域だ。残った2通りの重なり合う領域(4, 5)が、問題の発生源だろう。

まず、共有コンポーネントのコンテキストと、単一のバリューストリームが重なる領域(4)は、それぞれアプリケーション側のコンテキストであるべき可能性が高い。共有コンポーネントがそれを侵犯してしまったために、アプリケーションのバリューストリームが流れ込んでしまっていると考えられる。コンテキストの境界を最初から正確に見極めることなどできないので、これは仕方がない。共有コンポーネントのコンテキストから徐々に切り離していけば良さそうだ。

次に、共有コンポーネントのコンテキストと、複数のバリューストリームが重なる領域(5)は、どう考えるべきだろうか。ここは、共有コンポーネントに対する要求がコンフリクトしやすい箇所とも言える。アプリケーションごとにその使われ方、つまりユースケースやユーザーストーリーが異なるからだ。共有コンポーネントチームがその仲裁に入っているために、いつまで経ってもチーム間が密接に協力し合うコラボレーションモードのインタラクションが続いているのだろう。従属アプリケーションの数がさらに増えれば、コミュニケーションコストが増大することは目に見えている。これはまずい。

要求のコンフリクトが発生する箇所は、それぞれの要求に対する柔軟性が必要な箇所であるとも言える。設定によるカスタマイズ性や、イベントのハンドリング手段、プラグイン機構などを提供すれば、柔軟性を持たせることができる。こういった仕組みを提供することは、チーム間をX-as-a-Serviceモードのインタラクションに移行させ、コミュニケーションコストを下げることにもつながる。

いや、要求のコンフリクトが発生しないこともあり得る。むしろその方が多いのかもしれない。しかしこれを共有コンポーネントチームが一手に引き受けて開発する方式は、ボトルネックになりやすい。

それならば、要求を持つ従属アプリケーションチームが、共有コンポーネントに変更を加えても良いのではないか。コントリビューターとして実装し、コミッターたる共有コンポーネントチームにプルリクを送るという、OSS開発のようなチーム間インタラクションが実現できれば、負荷を分散できる。コントリビューター/コミッターモードでのインタラクションとでも言えば良いだろうか。

もちろん、要求はイシュー管理し、仕様や設計方針は、イシューやプルリクに対するコメントで議論する。共有コンポーネントに関するドキュメントを整備し、テストもデプロイも自動化しておくことが必須になるだろう。

マルチバリューストリームから解放されるために共有コンポーネントチームがやるべきことが見えてきた。

  • 共有コンポーネント独自のコンテキスト領域(1)の開発を進めていく
  • 共有コンポーネントのコンテキストと、単一のバリューストリームが重なる領域(4)を切り離していく
  • 共有コンポーネントのコンテキストと、複数のバリューストリームが重なる領域(5)を次のいずれかで対応する
    • 要求のコンフリクトが発生する箇所は、カスタマイズ可能にする
    • 要求のコンフリクトが発生しない箇所は、コミッターとして、アプリケーションチームからのプルリクを受けつける

これらの対応を進めるにあたり、アプリケーションチームも対となる対応が求められる。共有コンポーネントから切り離されたコンテキストをアプリケーションに組み込むことや、共有コンポーネントのカスタマイズを進めることなどだ。共有コンポーネントはバージョニングされているので、これらの対応の多くは、共有コンポーネントチームとアプリケーションチームの間で非同期に進められる。

そうは言っても、共有コンポーネントと従属アプリケーションの境界にこれだけの変更を加えるとなると、意図しないトラブルが発生し得る。共有コンポーネントチームはそれを不安に感じ、コンポーネントの変更にためらいを感じるかもしれない。

その不安を軽減するために、アプリケーションチームがテストコードを共有コンポーネントチームに提供しておくと良いだろう。共有コンポーネントに対する担当アプリケーションの要件を、テストコードに書いて引き渡し、共有コンポーネントのビルドパイプライン内でテストが実行されるようにしておく。そうすれば、トラブルを未然に防ぐことができる。マイクロサービスアーキテクチャで言うところのconsumer-driven contrat testingだ。

アプリケーションチームには他にもやるべきことがある。共有コンポーネントの変更に対する担当アプリケーション側での変更コストを最小限にとどめることだ。依存先の変更に影響を受けること自体は仕方のないことだが、それがアプリケーションの広範囲にわたるならアーキテクチャに問題がある。影響範囲を特定することが困難になり、修正箇所に抜け漏れが発生しかねない。

この問題は、依存性逆転の原則(DIP, Dependency Inversion Principle)に従うことで軽減できる。「抽象に依存せよ」というやつだ。それぞれのアプリケーション側で、自身が必要とする機能を抽象(インタフェース)として定義し、その詳細(実装)の中で共有コンポーネントに依存させる。こうすることで、アプリケーションの下位レベルが依存する共有コンポーネントの仕様が、上位レベルのコードを汚染することを防ぐ。共有コンポーネントに対する変更は、この詳細の中で制御可能になる。また、抽象があればテストダブルを導入できる。テストにかかるコストも軽減できるだろう。

依存性逆転の原則
依存性逆転の原則

結論は無い

やるべきことは見えたが、これはあくまでも机上の論だ。マルチバリューストリームで苦労していた他社事例を題材に(多少、肉付けしたが)、どう対処できるかを本エントリを書きながら考えてみたものだ。この事例がその後、現実世界でどうなったのかは聞いていない。

ここに書いたことを実際に実行しようとすると、より難解な問題が多くたちはだかると思う。現実問題として、そもそもこれらを進めていく間も、マルチバリューストリームは流れ続ける。共有コンポーネントチームがその対応に追われていては、状況はいつまで経っても変わらない。崩すべきはまずここからだろう。やり方はいくつか考えられるが、これ以上は論を重ねても実行性がなくなりそうだ。考えるのは、この辺りでやめておこう。

バッチサイズ削減はソフトウェアデリバリに何をもたらすか

「処理能力向上」と「バッチサイズ削減」。ソフトウェアデリバリのリードタイムを短縮したい組織の多くは前者、すなわちチームの処理能力向上に注力する。それが、ソフトウェアエンジニアリングに携わる我々が長年常識としてきたやり方だ。バッチサイズ削減について考えることなどなかった。むしろ、バッチサイズを大きくした方が効率的とさえ考えられてきた。

チームの処理能力向上はもちろん重大な関心事であるが、その実現難易度は高い。時間やコストを要する。それに比べ、バッチサイズ削減は、関係者の合意さえ得られればすぐにでも実現できる。そうであるにも関わらず、バッチサイズ削減という選択肢が軽視されるのは、バッチサイズが及ぼす影響の可視性が著しく低いためではないか。

ドナルド・ライナーセン(Donald G. Reinertsen)は、バッチサイズ削減が、リードタイム短縮やフローにおける変動低減といった効果をもたらすことを挙げている

プロセス / フロー / フローユニット

バッチサイズへの理解を深めていく前に、「プロセス」とは何であるかを定義しなければならない。

ソフトウェアデリバリのプロセスは凡そ、実装/コードレビュー/ビルド/テスト/デプロイといった、順序付けられたいくつかのステージが連なるパイプラインとして構成されている。プロダクトバックログから取り出されたアイテムは、それを構成するいくつかのアイテムに細分化された上で、それぞれがこのパイプラインのステージを順に進んでいく。このような、アイテム移動の様を「フロー(flow)」と呼び、移動していく個々のアイテムを「フローユニット(flow unit)」と呼ぶ。

f:id:mtx2s:20220307064842p:plain

「プロセス」の概念で重要なのは、どこからどこまでをプロセスとするか、観察者がその範囲(システム境界)を任意に定義できる点にある。デリバリパイプライン全体をひとつのプロセスとすることはもちろん、ステージひとつをプロセスとして扱うこともできるし、連続する複数のステージをひと括りでプロセスとして観察することもできる。

プロセスの範囲によっては、フローユニットの粒度にも選択肢がある。デリバリパイプライン全体をプロセスとして観察するなら、トピックブランチに切り出して実装するレベルのアイテムをフローユニットとしても良いし、プロダクトバックログアイテム(ストーリーなど)をフローユニットとしても良い。

WIP数 ≠ バッチサイズ

プロセスにはイベントがある。プロセスへのフローユニットの「到着」と、プロセス内で処理を終えたフローユニットの「出発」だ。到着から出発までの期間にあるフローユニットや、そのフローユニットに対する作業がいわゆる「WIP(Work In Process, Work In Progress, 仕掛り作業)」と呼ばれるものだ。「DIP, Design In Process」と呼ばれることもある。

f:id:mtx2s:20220308091313p:plain

コードレビュープロセスであれば、プルリクとして投げられたトピックブランチ上の変更(changes)が、フローユニットでありWIPということになる(WIPは仕掛りの「作業(work)」なのだから、正しくは、変更に対する「コードレビュー」作業をWIPと言った方が良いのかもしれない)。

誤解しそうになるが、WIPの数はバッチサイズじゃない。WIP数は単に、ある時点におけるプロセス内のフローユニットの数を示しているに過ぎない。「バッチ」とは、フローユニットのコレクションのことではあるが、むしろ、プロセスに対してひとまとまりで到着したり、出発する単位だと捉えた方が理解しやすい。そのコレクションに含まれるフローユニットの数こそが、本稿の主役である「バッチサイズ」だ。

下図は、バッチによるプロセスからの出発の様子をCFD(Cumulative Flow Diagram, 累積フローダイアグラム)で描いたものだ。

f:id:mtx2s:20220309222556p:plain

CFDは、時系列でのフローユニットの増加を累積で描いた面グラフで、イシュー管理ツールに付属するレポート機能などで馴染みがあるのではないだろうか。ここでは到着したフローユニットの数をグリーン、出発したフローユニットの数をブルーで表現している。

グリーンとブルーに挟まれた垂直方向の長さは、その時点でのWIP数を示している。それが時間の経過とともに徐々に大きくなっていき、バッチが出発したタイミングで一気に小さくなる。そしてまた徐々に大きくなっていく様子が読み取れる。

WIP数の増加によるフロー効率とリードタイムの悪化

プロセス内のフローの効率性を考える上で、プロセスリードタイムは重要な指標だ(以降は単に「リードタイム」と呼ぶ)。リードタイムとは、ひとつあたりのフローユニットがプロセスに到着してから次へ出発するまでの時間で、WIP時間とも言える。

f:id:mtx2s:20220308091340p:plain

ここに、処理能力(processing rate, service rate)が1時間あたり平均1/3個のプロセスがある。プロセスが空の状態であれば、フローユニットが1個だけ到着すると、出発するまでのリードタイムが3時間であるこということだ。この期間、WIP数が1個であることに注意したい。

f:id:mtx2s:20220312080407p:plain

同じプロセスにおいて平均3個のWIPがあるなら、平均リードタイムはどうなるか。

f:id:mtx2s:20220312080432p:plain

9時間となり、3倍にのびる。リトルの法則からも明らなように、平均リードタイムは平均WIP数に比例するからだ。

平均リードタイム = 平均WIP数 / 平均処理能力

f:id:mtx2s:20220308082745p:plain

WIP数が3個の時のリードタイムである9時間のうち、付加価値時間(value-added time, service time)はその約33%の3時間だけだ。残りの約67%にあたる6時間は、単なる待ち時間(wait time, queue time)にあてられている。つまり、リードタイムに対する付加価値時間の割合が高いほど、フローの効率性が高いと言うことだ。このような、リードタイムに占める付加価値時間の割合を「フロー効率(flow efficiency)」と言う。

フロー効率(%) = 付加価値時間 / リードタイム

リードタイムとフロー効率に影響を与えるWIP数の変化は、いったい何によって生じているのだろうか。その代表的な要因が、バッチサイズなのだ。

余談ではあるが、プロセスリードタイムは「サイクルタイム(cycle time)」と呼ばれることが多いようだ。しかし、文献によっては、「スループットタイム(throughput time)」と呼ばれ、サイクルタイムが別の意味で使われることもある。このため、本稿では「プロセスリードタイム」あるいは単に「リードタイム」と呼ぶことにした。

バッチ出発によるWIP数の増加

プロセスを出発するフローユニットのバッチ化がリードタイムに及ぼす影響をみるために、バッチサイズの異なる2つのケースを考えてみる。それをCFDとして下図に描いた。書籍『The Principles of Product Development Flow: Second Generation Lean Product Development』の例を参考にしている。

f:id:mtx2s:20220310000919p:plain

先ほども述べた通り、グリーンとブルーに挟まれた垂直方向の長さは、時系列でのWIP数の変動を表しているのであるから、その合計であるグリーン領域の面積が大きいほど、平均WIP数が大きい。その面積はバッチサイズに比例するので、平均WIP数は、左のCFDの方が大きい。

リトルの法則にある通り、平均リードタイムは平均WIP数に比例する。これらのことから、バッチサイズが大きいほどリードタイムが大きくなってしまうことがわかる。

バッチ到着による高稼働率の常態化とWIP数の増加

大きなバッチとしてフローユニットのかたまりがプロセスに押し寄せれば、WIP数が一気に跳ね上がる。リードタイムが悪化するのは想像に容易いが、プロセスへの影響はそれだけではすまない。

到着のバッチサイズが大きくなる理由はいくつも考えられる。その主要なもののひとつに、稼働率(utilization)を上げようとする意図はないだろうか。それが、高い稼働率を常態化させる。しかし、高稼働率の常態化は、リードタイムの指数関数的な悪化を招いてしまう。

まず、「稼働率」とは、時間あたりに到着するフローユニットの数である「到着率」を、時間あたりに処理できるフローユニットの数である「処理能力」で割った値を言う。

稼働率(%) = 到着率 / 処理能力

例えば、フローユニットがプロセスに到着する間隔が5時間に1個であれば、到着率は1時間あたり0.20個となる。処理能力は、フロー効率が100%である場合のリードタイムが4時間であるとすると、1時間あたり0.25個となる。この時の稼働率は、80%ということになる。

稼働率が100%未満である時の、稼働率とWIP数との関係は、次のグラフのようになる。稼働率が100%に近づくほど、WIP数が大きく跳ね上がる様子がわかる。

f:id:mtx2s:20220310223759p:plain

リードタイムはWIP数に比例するため、高稼働率にあるプロセスはリードタイムが長い。それは、フロー効率が低い状態でもある。このことから、稼働率とフロー効率の両方を同時に上げることが困難であることが理解できる。このような、フロー効率と稼働率(リソース効率)の関係性を、二クラス・モーディグ(Niklas Modig)とパール・オールストローム(Par Ahlstrom)は、「効率性のパラドックス(efficiency paradox)」と呼んだ。

モーディグらも言うように、効率性のパラドックスは、もう一つの要素である変動性によってより深みにはまる。

変動性と稼働率がリードタイムに与える影響

稼働率とリードタイムの関係は、プロセスが抱える「変動性(variability)」に影響を受ける。「ばらつき」と言った方がイメージしやすいだろうか。その要因は、「リソース」「フローユニット」「外部要因」の3つに分けることができる。

リソースであれば、プロセスを稼働させているチームメンバーの体調やモチベーションによる影響もあり得るし、ビルド環境が壊れることもあるかもしれない。フローユニットの変動として真っ先に思い浮かぶのは、個々のストーリーや機能の開発規模のばらつきだろう。外部要因であれば、割り込みも含め、フローユニットの到着のばらつきが考えられる。

このような変動がまったく無いプロセスはあり得ないが、プロセスによって高変動か低変動かの差はある。次の図は、高変動にあるプロセスと、低変動にあるプロセスに関する稼働率とリードタイムの関係を描いたグラフだ。

f:id:mtx2s:20220310223832p:plain

高変動にあるプロセスの方が、低変動にあるプロセスより、稼働率がリードタイムに与える影響が大きくなることがわかる。

バッチ処理によるマルチタスクとオーバーヘッド

フローユニット到着のバッチ化は、プロセス内での処理をマルチタスキングに導く。今や誰もが知るように、タスク切り替えによるスイッチングコストは、リードタイムに影響する。

ジェラルド・ワインバーグ(Gerald M. Weinberg)によると、タスクの並列数が2になると、スイッチングコストによって稼働時間の20%をロスし、並列数が3だと40%をロスするという。これも変動と言えるだろう。

f:id:mtx2s:20220311064831p:plain

処理能力向上とバッチサイズ削減

以上のように、リードタイムは、付加価値時間だけで占められた時間ではない。待ち時間やマルチタスクでのスイッチングコストといった、"非"付加価値時間が多分に含まれている。バッチサイズ削減を通したWIP数のコントロールは、非付加価値時間を削減し、フロー効率を高めようとするアプローチなのだ。

では処理能力向上はと言うと、価値をより短時間で付加する能力を得ようとすることであり、付加価値時間の圧縮を目指すアプローチだと言える。本稿冒頭に書いた「処理能力向上だけでなく、バッチサイズ削減にも目を向けなくて良いのか」という問いは、言い換えれば、「付加価値時間圧縮だけでなく、非付加価値時間削減に目を向けなくて良いのか」という問いでもある。

デヴィッド・アンダーソン(David J. Anderson)によれば、ソフトウェアデリバリのパイプライン全体のフロー効率は概ね1%から25%の範囲に入るようだ。これはつまり、付加価値時間の圧縮効果はリードタイム全体の1%から25%程度であり、非付加価値時間の削減効果は75%から99%ものポテンシャルを秘めているということでもある。バッチサイズ削減に取り組まない手はないだろう。

バッチサイズ削減を突き詰めようとすると、トピックブランチを統合ブランチにマージして以降のフローが自動化され、かつ疎結合であることが鍵となる。ここのコストが高いと、バッチサイズを大きくしようとする力が働く。目指す究極は、継続的デプロイ(continuous deployment)による一個流しだ。そのためにはニコール・フォースグレン(Nicole Forsgren)らが言うように、「テスト、デプロイの自動化」とあわせて「デプロイとテストの容易性」に注力すべきであることは言うまでもない。

技術的負債は開発者体験を悪化させる

ソフトウェアエンジニアにとって、技術的負債が増え続けるソフトウェアプロダクト開発現場に身を置くことがどれほど苦痛なことであるか。エンジニアリング組織のマネジメントを長年担ってきて、それは強く感じるところだ。

中途採用の選考プロセスに面接官として参加し、これまで数多くの退職理由を見聞きしてきた。その中で、レガシーシステムリファクタリング・リアーキテクティング・リライトできないことへの不満を理由として挙げるエンジニアは多かったように思う。裏を返せば、自社のソフトウェアプロダクトが技術的負債にまみれたまま放置されているなら、優秀な人材が他社に流出するリスクがあると認識すべきだ。

本稿では、技術的負債と開発者体験の関係について紐解くとともに、それに対してソフトウェアエンジニアリング組織を預かるマネージャーが取るべき行動について考えてみたい。

※これは、Engineering Manager Advent Calendar 2021 の21日目の記事です。

バックログアイテムの分類にみる不可視領域と開発者体験の関係

下の図は、バックログに含まれるアイテムをカテゴライズし、4色に色分けしたものだ。フィリップ・クルーシュテン(Philippe Kruchten)によって2009年頃に考案され、"What Colo(u)r is Your Backlog?" というタイトルで発表された。ここでは2013年に彼のブログ記事で紹介されたバージョンに基づいている。技術的負債(technical debt)を返済するためのアイテムは、右下の黒いエリアにカテゴライズされている。

Four Colors in a Backlog

四象限の左側にカテゴライズされたバックログアイテムは、ソフトウェアプロダクトの「振る舞い(behavior)」を変える・正すものだ。振る舞いは目に見える(visible)。「振る舞いを変える/正す」目的は、「ユーザー体験(UX, User eXperience)」を向上・改善することだろう。

対となる右側にカテゴライズされたバックログアイテムは、ソフトウェアプロダクトの「構造(structure)」を変える・正すものだ。構造は目に見えない(invisible)。「構造を変える/正す」目的は、振る舞いの「変更容易性(modifiability)」を向上・改善することだろう。

「振る舞いを変える/正す」のはエンジニアの役割なのだから、ソフトウェアプロダクトの変更容易性の良し悪しは、エンジニアの体験に影響を与える。「構造を変える/正す」意思決定が適切になされないと、「開発者体験(DX, Developer eXperience)」が悪化することが想像できる。

振る舞いと構造、ユーザー体験と開発者体験

フィリップ・クルーシュテンの同ブログ記事にも書かれているように、右上の黄色いエリアである「アーキテクチャ(architectural, structural features)」にカテゴライズされるアイテムに手を付けない選択は、右下の黒いエリアである「技術的負債」にカテゴライズされるアイテムの増加につながってしまう点も留意すべきだろう。

Failure Demand という利子の支払いに追われるエンジニアの疲弊

変更容易性の悪化が、ソフトウェアデリバリのリードタイム(lead time for changes)を悪化させることは言うまでもない。マーティン・ファウラー(Martin Fowler)によれば、コード品質を高く保つコストを削ることで生産性を上げる試みが通用する期間、いわゆる "design payoff line" に到達するまでの時間は、せいぜい数週間以内だと言う

それに加え、複雑化しすぎたコードを変更することは、多くのバグを生み出しやすい。それがリリース後の障害となり、変更失敗率(change failure rate)を押し上げる。こうしてエンジニアは度重なる障害対応に追われ、精神をすり減らし、疲弊していく。

このような、本来やるべきことをやらなかったために後から強いられる労力のことを "failure demand" と言う。技術的負債のメタファーで言えば、「利子を支払う」といったところだろう。この利子の支払いは、エンジニアが新たな価値を生み出す "value demand" のための時間を奪い、開発現場は益々混乱していく。書籍『LeanとDevOpsの科学』によれば、ローパフォーマーに分類される組織は、ハイパフォーマーに対して「予定外の作業や修正作業」にかかる時間比率が6ポイントも高い。

新たな作業と予定外の作業等の比率

経営者やビジネスマネージャーからすれば、少しの振る舞いの変更に、なぜこれほどまでに時間がかかるのか理解できない。その上、障害まで頻発する。こんなことが続けば彼らは、エンジニアに対して不満と不審感を抱くようになる。その感情はエンジニアにも伝わる。エンジニアはきっと、やり切れない気持ちになるだろう。

開発者体験の悪化が組織を単なる集団に変えていく

2012年に行われたGallupの調査では、従業員エンゲージメント(employee engagement)の高さで上位4分の1に入る企業は、下位4分の1の企業に対し、顧客評価で10%, 生産性で21%, 収益性で22%上回った。また、欠勤は37%, 離職率は25%から60%、品質上の欠陥は41%下回った。

従業員エンゲージメントによるパフォーマンスの比較

従業員体験(EX, Employee eXperience)は、従業員エンゲージメントに影響すると言われている。開発者体験も同様だろう。

先述したように、「構造を変える/正す」ことを軽視することは、リードタイムや変更失敗率といったソフトウェアデリバリのパフォーマンスを悪化させる。しかし、従業員エンゲージメントに関する調査結果からもわかるように、開発者体験が悪ければ、ソフト面からビジネス成果や組織の持続可能性を押し下げることになる。

これは、チェスター・バーナード(Chester I. Barnard)の言う組織の3要素のうちのひとつである「協働意思(willingness to serve)」が失われるからだろう。組織が組織たり得ず、単なる人の集まり、つまりは「集団」の状態に陥ってしまうのだ。

技術的負債が増え続ける背景

このような問題を生み出す背景のひとつには、「振る舞いを変える/正す」ためのバックログアイテムばかりが優先順位の上位に並んでいるという状況がある。「構造を変える/正す」ためのバックログアイテムに着手される日は、なかなかやってこない。

優先順位の決定権者にとっては、ユーザー体験を高め、プロダクトのユーザー価値を高めることこそが優先課題だ。それが、プロダクトのビジネス価値向上につながる道だからだ。ソフトウェアの「構造を変える/正す」ことに取り組んだところで、少なくとも短期的にはビジネスに良い影響を与えるように感じられない。そんなものは少しでも後回しにして、「振る舞いを変える/正す」ことに集中したい。

エンジニアの不満は主にここに集中しがちであるが、技術的負債が増え続ける主たる理由は本当にこれだけだろうか。

それを知るためには、技術的負債と呼ばれるコードを自分の目で確かめると良い。そうすると、別の根深い問題が潜んでいることに気づく。とんでもなく低い技術レベルで書かれたコードが数多く紛れ込んでいるからだ。

書籍『How Google Works』によれば、Google(Alphabet)社には、「採用の質を犠牲にしてまで埋めるべきポストはない」という不可侵な黄金率があるという。採用において、速さか質かという選択肢が迫られる場面では、必ず質を選ぶことで優秀なエンジニアの獲得にこだわる。

しかし多くの企業では、採用でそこまでの質を追求できないのが実情だろう。世界屈指のテック企業と比べたら、そもそもの応募者数が違う。結果、社内のエンジニアのスキルは玉石混交、優秀なエンジニアもいれば、プログラミング言語仕様やプログラミングパラダイムを十分に理解していないエンジニアも存在することになる。社内にエンジニアが増えていく企業フェーズでは特に、こういったことが起こりやすい。

そんな状況で、均一的に質の高いコードを維持することは難しい。エンジニア育成にも時間がかかる。数少ない優秀なエンジニアだけでこの状況をカバーすることは不可能だ。

つまり、技術的負債が増え続ける主な背景はふたつ。ひとつは、返済する意思のない無計画な借金を積み重ねること。もうひとつは、一度の借り入れ額が大きくなりやすい問題(技術スキルの低さ)を抱えていること。

マーティン・ファウラーは、前者を「意図的(deliverate)」かつ「無謀(reckless)」な負債と呼び、後者を「無自覚(inadvertent)」かつ「無謀(reckless)」な負債と呼んだ。下の四象限の左上と左下がそれにあたる。

技術的負債の発生理由に関する分類

コード品質にもSLOとエラーバジェットを

さて、まずは「意図的」かつ「無謀」な技術的負債への対処方法を考える。

いつまで経っても優先順位の上がらない「構造を変える/正す」ためのバックログアイテムに着手することを、関係者から合意を取り付けようとする開発チームの交渉は、なかなか上手くいかないことが多い。そうであればその大役は、マネージャーの責務であると考えるべきだろう。

しかし、その実践はなかなかに骨が折れる仕事だ。関係者との力関係も影響するし、強力で洗練された説得力も必要になる。その上、こういった交渉の必要性は一度だけでなく、度々発生するであろうことを考えると、対応コストが高すぎる。マネージャーが開発プロセス上でのボトルネックになりかねない。どこかに優先順位付けのための良いバランスツールはないのだろうか。

SRE(Site Reliability Engineering)に、「エラーバジェット(error budget)」というものがある。例えば、あるAPIへのリクエストの95%のレスポンスタイムが400ミリ秒以下であることをSLO(Service Level Objective)としていた場合、400ミリ秒を超えるリクエストが5%未満の間はサービスレベルとして許容できる。この許容幅をエラーバジェットと言う。この考え方が、技術的負債についても応用できそうだ。

エラーバジェットの優れている点は、システムの変更を担う開発サイドと、システムの信頼性を担う運用サイドの対立を、両者の力関係ではなく客観的なメトリクスで解決した点だ。エラーバジェットが枯渇するまではシステムを自由に変更できるが、許容値を超えると機能リリースを止め、信頼性回復に注力する。

このSLOやエラーバジェットのようなものを、コード品質にも取り入れたい。コード品質は静的コード解析ツールなどを利用してメトリクス化できる。そのメトリクスを使ってSLOを策定し、バックログの優先順位決定権者と握っておくのだ。そうすれば、「振る舞いを変える/正す」ことによって生じた技術的負債の量がバジェットを超えた時、開発チームは、「構造を変える/正す」ことに着手できるようになる。

なお、既存のソフトウェアプロダクトのコード品質をメトリクス化すると、最初はひどい結果になる。導入初期段階でのSLOは低めに設定し、徐々に理想的なラインに近づけていく運用にすると良いだろう。

さてこれで、我々マネージャーはコストの高い交渉業務から解放された。やるべきことはただ、この説得力の高いバランスツール導入への同意を関係者から取り付けるだけとなった。

負債ベースラインと負債上限によるポリシー策定

書籍『リーン開発の現場』の著者でもあるヘンリック・クニバーグ(Henrik Kniberg)が、技術的負債のマネジメント手法について、「負債ベースライン(debt baseline)」 と 「負債上限(debt ceiling)」による許容範囲を設けることをブログ記事で提案している。技術的負債がこの許容範囲内に入るようマネジメントしようというものだ。負債上限を超えたら全ての機能開発を止めて、クリーンアップに専念する。この手法が、コード品質のSLOやエラーバジェットの導入にちょうど合う。

負債上限と負債ベースライン

ベースラインがある理由は、技術的負債の最後のひとつひとつを取り除いてゼロにする労力が大きすぎるからだ。

また、グラフがノコギリ歯状になっている理由は、振る舞いを変更するバックログアイテム、つまり新機能が提供する価値を仮説だと考えているからだ。リリース後の検証結果によってはその機能の提供を中止するかもしれないし、大幅に方向性を変えるかもしれない。それを前提とすると、仮説である間はコード品質を求め過ぎるのも非効率だ。検証によって価値が証明されてからクリーンアップするのが効率的だろう。これをサイクルとして繰り返すことが、ノコギリ歯で表現されている。それでも取りこぼしがあるだろうから、少しずつ技術的負債が増えていくという構図だ。

ちなみにここでは、素早く仮説検証するために生じた、数日から一週間以内の技術的負債を「良い技術的負債(good technical debt)」と呼んでいる。「意図的」かつ「慎重」な負債にカテゴライズされるものだろう。

技術的負債やコード品質のメトリクス化は、先述の通り静的コード解析ツールを利用するのが良いと思うが、そのメトリクスも完全なものとはならない。ヘンリック・クニバーグの同ブログ記事にもあるように、現場エンジニアの意見を取り入れるのも良さそうだ。

高い保守性というコーディング指針の言語化

次に、「無自覚」かつ「無謀」な技術的負債への対処方法を考える。もちろんエンジニアの技術力の底上げも必要であるが、本稿の趣旨とは外れるためここでは触れないこととする。

コード品質に対するSLOやエラーバジェットの導入は、コード品質の可視化と基準を明確にする。しかし、どのようにしてその基準を達成するのかについては明らかにしていない。つまり、どのように「コーディング」すれば、求めるコード品質となるのかが不明瞭だ。

残念ながら、静的コード解析のメトリクスが良いスコアになったからといって、それが必ずしも「コード品質が高い」ことを表しているとは言いきれない。それでなくても数値目標は、自己目的化しやすい。数値を追いかけるだけになってしまっては、目的は達せられない。どういうコードが「品質が高い」と言えるのか、それはどのようにコーディングすれば到達できるのか、開発チームと共同で言語化し、指針を明確にすべきだろう。

技術的負債をクリーンアップする方法と言えば、「リファクタリング(refactoring)」が真っ先に思い浮かぶ。指針を検討する上で、これが糸口となりそうだ。

リファクタリングの目的は、コードの変更容易性の向上・改善に加え、「理解容易性(understandability)」を向上・改善することだ。コードを変更するには、コードを読み、変更対象となる箇所を特定する必要がある。理解容易性が高いコードは、この探索活動の難易度を下げてくれる。

そして、リファクタリングには自動テストが付きものだ。変更後のコードが仕様通り動くことを簡単に確認するためだ。ここで、「テスト容易性(testability)」が向上する。

このように、リファクタリングによって得られる効果、すなわち「品質の高いコード」とは、変更容易性・理解容易性・テスト容易性が高いコードに他ならない。バリー・ベーム(Barry Boehm)らは、これらの品質特性をまとめて、「保守性(maintainability)」と呼んだ

保守性と3つの品質特性

目指すべき品質の高いコードとは、変更容易性・理解容易性・テスト容易性を兼ね備えた保守性の高いコードであることは分かった。これは、構造を変える・正すためにリファクタリングする時だけではなく、振る舞いを変える・正すためにコードを書く時にも言えることだ。そして同時に、お馴染みの手法や活動の目的も次のように明確になる。

  • 変更容易性・理解容易性・テスト容易性の高いコード設計を導く「TDD(Test-Driven Development, テスト駆動開発
  • テスト容易性を上げる「テスト自動化(test automation)
  • 保守性の高さを観点とした「コードレビュー」、または「ペアプログラミング」「モブプログラミング

特に、いくつかのチームリーダーやマネージャーにコードレビューの観点についてヒアリングしたところでは、明確な基準が存在していないケースが多かった。それでは一貫したコード品質は得られない。保守性の高いコードとはどういうものであるかをチームで具体化し、それを観点とするレビューが実施できる状態を目指したい。

なお、「どのようにコーディング」コーディング指針やモジュールの依存関係に関するルールなどをテストコードにすることもできる。こちらについては、記事『ArchUnitでアーキテクチャをテストする - mtx2s’s blog』に書いた。

mtx2s.hatenablog.com

継続的インスペクションによるコードレビュー負荷の軽減と手戻りの防止

ところで、コードレビューには2点ほど問題があると考えている。

ソフトウェアデリバリのリードタイム改善を進めていた時に気付いたのだが、コードレビュー待ちで止まってしまうワークアイテムがそれなりに多い。レビュアーが複数のレビューを抱えていたり、自身もコーディング中のワークアイテムを抱えていたりして忙しいからだ。

ある調査によると、コードレビューの効果を高めたいなら、「一度にレビューするのは400LOC未満」「一時間あたり500LOC以下」「一度に60分以上のレビューをしない」のが良いらしい。それなりに時間がかかる。忙しい身で品質の高いレビューは難しい。これが一つ目の問題だ。

二つ目の問題は、コードレビューがコーディングの最後に行われる点だ。大きな問題が見つかった時の手戻りが痛い。

これらの問題の対策として、ペアプロやモブプロも良いのだが、あわせて「継続的インスペクション(continuous inspection)」の導入も検討したい。せっかく静的コード解析ツールを導入したのなら、それをCIのパイプラインに組み込み、コーディング中、プルリク時、ビルド時に自動で検査すれば良い。そうすることで、レビューによる負荷も軽減されるし、コーディング段階からコード品質を検査することが可能になる。

技術的負債の原因の多くはアーキテクチャ選定の失敗

Software Engineering Institute(SEI)が、大企業3社(39ビジネスユニット)で働くソフトウェアエンジニアとアーキテクトを中心とする1,831名を対象に行った調査によると、技術的負債の原因として回答者に最も多く選択されたのが「アーキテクチャ選定の失敗」だった。

下のグラフは、14の選択肢をプロジェクトにおける負債の量でランク付けするよう求めた結果を、1位、2位、3位のいずれかに選んだ回答者の数で集計したものだ。

技術的負債の原因ランキング

アーキテクチャを入れ替えるには、大規模なリファクタリング、いや、おそらく「リアーキテクティング(rearchitecting)」が必要になる。

リアーキテクティングは時間がかかる上に、場合によってはその実施期間中、新機能開発を止めなければならないこともある。プロダクト開発に対してそのような厳しい制約を課すバックログアイテムの着手には、優先順位決定権者も簡単には許可を出せない。

しかしそれは、提案したリアーキテクティング計画の着手時期が早すぎるからだ。ビジネスには四半期や半期、通期ごとに目標がある。優先順位決定権者は、リアーキテクティングの実施が目標達成に影響を及ぼすことを危惧している。

だから、リアーキテクティングの実施を、目標達成に影響を及ぼさない時期にプロットすれば、意外とすんなり受け入れられる。次の目標策定時の計画に含められるからだ。そのためにもリアーキテクティング実施計画は十分に前もって準備を進めておきたい。

もちろん、リアーキテクティングを成功させるためには、十分な技術スキルとドメイン知識を持ったアーキテクトの存在と、その設計思想を適切にコードに変換できる能力がチームに求められることは、言うまでもないだろう。

最後に - エンジニアが組織やプロダクトに誇りを持つために

過去に、私がマネジメントすることになったばかりの組織のエンジニア数名に対して「もし、開発業務として自由な時間があるなら何がしたい?」と聞いたことがある。彼らの答えは概ね、担当する既存プロダクトのリファクタリング・リアーキテクティング・リライトのいずれかだった。これは、冒頭に書いた退職理由と同じだ。彼らは自分たちが作り上げたソフトウェアプロダクトやチームにも誇りを持っていなかった。むしろ否定的だった。

私は、自らが担当するエンジニアリング組織のミッションを、組織としての「プロダクト開発能力の差異化」だと定義している。市場においてプロダクトの優位性を獲得することに高い再現性のある組織を目指していると言えば伝わるだろうか。

mtx2s.hatenablog.com

その開発能力の源泉となるのは、チームやプロダクトに誇りを持って開発を続ける優秀なエンジニアたちだ。技術的負債の放置は、リードタイムや変更失敗率が悪化するというソフトウェアデリバリ面だけでなく、開発者体験、言い換えれば従業員エンゲージメントを悪化させる。これでは「プロダクト開発能力の差異化」の実現が遠のいてしまう。それだけに、技術的負債のマネジメントは、エンジニアリング組織を預かるマネージャーの重要な責務だと考えている。

追記:YouTube Liveでの登壇時のアーカイブ

本稿公開からちょうど1年後にあたる2022年12月21日に、本テーマをもとにイベント登壇した。タイミー社が主催するTech勉強会「技術的負債の返済から改善する開発者体験 - Techmee vol.5」でのゲスト公演枠で、その時の様子はYouTubeアーカイブされている。

www.youtube.com

timeedev.connpass.com

登壇時の資料はspeakerdeckに置いてある。

speakerdeck.com

クネビンフレームワークとソフトウェアエンジニアリング

2007年にクネビンフレームワークが Harvard Business Review で紹介されて以降、ソフトウェアエンジニアリングの世界では、アジャイル開発、特にスクラムを採用すべき理由の説明で本フレームワークが用いられるようになった。

リーダー向けツールとして開発された意思決定のためのフレームワークが、いったいどのようにして、ソフトウェアエンジニアリングの開発プロセスモデルや開発方法論を説明するというのだろうか。

本稿では、クネビンフレームワークがどういうものであるかを紐解きつつ、その具体例として、ソフトウェアエンジニアリングの現場に適用することを試みる。

クネビンフレームワークとは

クネビンフレームワーク(Cynefin framework)は、リーダーが直面する状況をカテゴライズし、そのそれぞれに適応した意思決定(decision making)プロセスを提供するツールだ。経営コンサルタントであり、ナレッジマネジメント複雑系科学の研究者である David J. Snowden らによって、1990年代終わり頃に第一世代が開発された。

その後、現在の第三世代が、"A Leader’s Framework for Decision Making" というタイトルで英語版 Harvard Business Review の 2007年11月号にて紹介されると、翌2008年の Outstanding Practitioner-Oriented Publication in OB アワードを受賞するなど、大きな反響を読んだ。この時の記事は、日本語版ハーバード・ビジネス・レビューの2008年3月号で『臨機応変の意思決定手法 「クネビン・フレームワーク」による』として読むことができる。

フレームワークはその後も進化を続けているが、本稿では基本的に、この日本語版ハーバード・ビジネス・レビューの2008年3月号で紹介されたバージョンをもとに書いている。

クネビンフレームワークでは、取り扱う問題が置かれた状況を次の五つの領域(domain)に分類している。

  • Simple / 単純な状況
  • Complicated / 込み入った状況
  • Complex / 複雑な状況
  • Chaotic / カオスな状況
  • Disorder / 無秩序

この五つの領域は、状況を生じさせている原因と結果の因果関係の明確さの度合いによって分類されている。そして、その領域それぞれに対し、意思決定のためのプロセスモデルが定義されている。

Simple / 単純な状況にある領域

この「単純(Simple)」という呼び名は、実はもう使われていない。2021年10月現在では、「明白(Clear)」と呼ばれ、その前は「単純(Obvious)」と呼ばれていた。いずれにしても、現時点では日本語での名称が未定義のようなので、クネビンフレームワークを紹介するドキュメントによって、若干の表記揺れがある。

単純な状況は、原因と結果の因果関係が誰の目にも明らかである問題領域だ。対応方法には必ずベストプラクティスが存在し、いずれを選択するかが重要だ。

この領域は、ソフトウェアエンジニアリングというより、アプリケーションを利用するシーンをイメージすると分かりやすい。

オンラインで開催される技術カンファレンスで、フロントエンド開発に関する5分間のライトニングトークを受け持つことになった。5分という短い時間とはいえ、トークだけで聴かせるのはなかなかハードルが高い。数枚のスライドを画面に映しながら話すことにしたので、プレゼンテーションソフトを使い、スライドを作り始めた。

ここでの意思決定プロセスモデルは、「把握 - 分類 - 対応(sense - categorize - respond)」だ。

  • 把握 - ライトニングトークのハードルを下げるため、スライドを使う
  • 分類 - プレゼンテーションソフトを使うことにした
  • 対応 - スライドを作った

エンジニアっぽい例を挙げたが、経営者の視点での例を挙げるなら、IT化が進んでいない企業が業務改善を目的としてオフィススイートやグループウェアを導入するといったケースも、単純な状況に該当するだろう。

Complicated / 込み入った状況にある領域

込み入った(Complicated)」は、「煩雑(Complicated)」と表記されることもある。

ここで扱う問題領域は、その原因と結果の因果関係を特定して適切な解決策を導き出すために、専門家の分析が必要になる。例として、業務システムの開発を考えてみよう。

事業規模の拡大によって非効率になった業務領域に対し、新たな業務システムの導入を検討している。ネットを使って様々なパッケージソフトを調べてはみたが、それらが問題を解決できるのかわからない。そこで、この業界を専門とするシステムコンサルに入ってもらうことにした。そして、コンサルによる業務分析の結果、いくつかのパッケージソフトを組み合わせ、カスタマイズした上で導入することになった。

ここでの意思決定プロセスモデルは、「把握 - 分析 - 対応(sense - analyze - respond)」だ。

  • 把握 - 事業規模の拡大によって業務が非効率になった
  • 分析 - 専門家による業務分析を行った
  • 対応 - パッケージソフトを組み合わせ、カスタマイズした上で導入した

ここで扱う問題は、当事者にとっては未体験でも、業界にとっては未知の領域ではない。様々な人が何度も対峙してきた問題であり、その対応法が型化され、いくつかのプラクティスとなっている。そうして体系化されたナレッジを有している存在である専門家が、分析によって適切なプラクティスを選び出し、より具体化された対応法を提示する。

つまりこの領域は、不確実性が低く予測可能性が高い領域だ。分析結果から得られた対応法が正しく問題を解決する可能性が高い。分析の品質が担保できればプロセスを後戻りすることもないので、段階的にプロセスを進めていくウォーターフォールモデルに向いた領域と言える。

しかし、ウォーターフォールでソフトウェアを作り上げた結果、ユーザーから「これじゃない」と言われたなら、対象としていた領域が実は複雑な状況であったのかもしれない。

Complex / 複雑な状況にある領域

複雑(Complex)」は、前述した単純な状況や込み入った状況とは違い、その状況を生じさせている原因と結果の因果関係が、後になって分かる領域だ。その因果関係を見出すために、失敗しても影響が小さい実験を繰り返し、そこから成功パターンを見つけ出す。

コンシューマー向けソフトウェアプロダクトの自社開発を例に考えてみよう。

ユーザー調査によって、ターゲットユーザーの日常生活をより便利にするための方向性が見えてきた。それを新機能としてリリースすることはできるが、想定通りのユーザー価値になる保証はない。大規模に開発してリリースした結果、使われなければ無駄だ。そこで、ユーザー価値があるという仮説を検証する最小限の機能(MVP, minimum viable product)を作り、ユーザーに使ってもらうことで、ユーザー価値の有無を確認することにした。そして検証の結果、ユーザー価値が十分に確認できなかったため、仮説を組み直して新たに検証を進めることにした。

ここでの意思決定プロセスモデルは、「探索 - 把握 - 対応(probe - sense - respond)」だ。

  • 探索 - MVP を作って検証した
  • 把握 - ユーザー価値が十分に確認できなかった
  • 対応 - 仮説を組み直すことにした

これは言うまでもなく、アジャイル開発系のソフトウェア開発方法論が得意とする開発プロセスだ。込み入った状況とは対照的に、不確実性が高く予測可能性が低い領域であり、実験を繰り返すことで正解となるパターンを見つけ出すことを重視している。

Chaotic / カオスな状況にある領域

カオス(Chaotic)」は、「混沌」と表記されることもある。

この領域は、その名の通りカオスだ。何が起きているのかを正確に把握することができない。状況は刻一刻と変わり続ける。得られる情報が断片的で、全貌が見えず、時間経過と共に様々な事象が検知され、報告されてくる。それ故、因果関係が明らかになることはなく、この段階で根本的な解決策を打ち出すことは不可能だ。

そもそも、解決策を見出すために探索したり、分析したりといった、そんな悠長な時間はない。止血することが最優先であり、とにかく、実効性のある一次対応を可能な限り打つことに集中する。そうして状況が安定してきたところで、ようやく根本的な解決策を見つけ出す活動に移行できる。

この状況をソフトウェアエンジニアリングの現場に当てはめてみると、真っ先に思い浮かぶのは、障害対応だろう。

企業向けに提供しているマルチテナントの SaaS プロダクトをアップデートし、新機能をリリースした。数時間後、ある顧客から「既存機能でエラーが出て、業務に必要なデータがダウンロードできない」という問い合わせが入った。他の顧客では同様の事象が発生していないようだったので、問い合わせのあった顧客が必要としているデータをひとまず手動で取得し、顧客に渡した。その直後ぐらいから、様々な機能からアラートが飛び始め、問い合わせが増え始めた。対応に追われ、社内はパニックだ。事象としては、いずれもデータベースとの接続で失敗しているようであった。しかし、調査・対策するにも時間がかかる。ひとまず今回のアップデートをロールバックすることで状況は落ち着いた。やはり、先程まで発生していた問題は、アップデートによる影響のようだ。被害状況を調査しつつ、原因の特定と対策に取り掛かった。

ここでの意思決定プロセスモデルは、「行動 - 把握 - 対応(act - sense - respond)」だ。

  • 行動 - 手動でのデータダウンロードや、ロールバックなどの手を打った
  • 把握 - ロールバックによって状況が安定したことで、アップデートによる影響であることがわかった
  • 対応 - 被害状況の調査と、原因の特定と対策を進めた

このように、カオスな状況では、唐突に未知なる状況に追い込まれ、その緊急対応に追われる。

ソフトウェアエンジニアリングでは、カオスな状況を意図的に生み出すことで、前もって未知な問題への対応能力を高め、ソフトウェアシステムの信頼性をより高めようとする手法がある。それが、カオスエンジアリングだ。命名の由来は異なるようだが、「カオス」と名付けられている点が興味深い。

Disorder / 無秩序な領域

無秩序(Disorder)」は現在、「混乱(confused)」と呼ばれているようだ。これは、現在の状況が、前述の四つの領域のいずれにあるのか、当事者が理解していない混乱した状態のことだ。そもそも、理解していないこと自体を本人が認識していない可能性もある。

この状態にある意思決定者は、自分の経験や好みに基づいて、意思決定プロセスモデルを選択する。よくある、アジャイル開発とウォーターフォールモデルの論争も、この辺りに起因しているのかもしれない。取り扱っている問題領域が複雑な状況にあるにもかかわらず、ウォーターフォールを採用している、といったように。さすがに、カオスな状況の中で、スクラムウォーターフォールを使って問題解決しようとするなんてことは無いと思うが。

プロセスモデルや手法の選択は、個々のモデルの長所・短所や、モデル同士の比較だけで決定するのではなく、取り扱う問題領域がいずれの状況にあるのかを理解し、そこから導きだすべきなのだろう。

スクラムの祖 野中郁次郎との関係性

クネビンフレームワークの構築にあたり、David J. Snowden 自身が、一橋大学名誉教授の野中郁次郎らの著書『知識創造企業』に刺激を受けたと述べている点が興味深い。

He also recalls being particularly provoked by Ikujiro Nonaka and Hirotaka Takeuchi’s book The Knowledge-Creating Company around converting tacit knowledge into explicit knowledge and around the whole Socialization, Externalization, Combination and Internalization (SECI) approach.

野中郁次郎と言えば、一般的には書籍『失敗の本質―日本軍の組織論的研究』の著者の一人として知られているが、ソフトウェアエンジニアリングの世界では、「スクラムの祖」としての方が知られているのではないだろうか。スクラムを作り上げたメンバーの一人である Jeff Sutherland が、その着想を得る原点が、野中郁次郎らが1986年に Harvard Business Review で公開した論文 “The New New Product Development Game” だ。

実際、Jeff Sutherland は、スクラムに関する自著で、スクラム理論の中心として David J. Snowden が参考にしたものと同じ書籍『知識造像企業』の「SECIモデル」を紹介しているようだ

このような点からも、クネビンフレームワークアジャイル開発の間接的な関係性がうかがえる。Cynefin.io にも、アジャイル開発に関する次のような記述がある。

Alongside this, the Cynefin® framework "took off" in a world of "Agile" software development, coming together with the "Agile Manifesto" in ways which have been "a rich source of ideas and practice ever since."

最後に - 複雑な領域に挑む「攻め」の姿勢とイノベーション

不確実性が低く、予測可能性の高い環境では、新しいものは生まれない。不確実性が高く、予測可能性の低い、複雑な状況にある領域に挑んでこそ、イノベーション創発される。では、その観点で日本の現状を見てみるとどうだろうか。

ガートナージャパンによる2018年5月の調査では、日本国内でのアジャイル開発の採用状況は 17% 程度にとどまっている。ウォーターフォールの採用状況である 43% と比較するとかなり低い状況だ。

これは、日本国内でのIT投資の対象が「込み入った領域」であり、よりイノベーティブな「複雑な領域」に踏み込めていない現状を表しているのではないだろうか。これが、日本のIT投資が「攻め」ではなく「守り」だと言われる所以だろう。調査結果からは、ウォーターフォール採用の縮小と、アジャイル開発採用の拡大が読み取れるため、これからは期待が持てるようにも読み取れる。

また、『DX白書2021』で、日本のアジャイル開発の採用状況をアメリカと比較してみると(『DXを支える手法と技術』p.213)、日本の 19.3% に対し、アメリカは 45.8% と、大きく差が開いている。インターネットの登場以降、GAFAをはじめとするアメリカのテック企業に日本企業が後塵を拝しているのは、ここに起因するのではないだろうか。

note.com

よく言われるように、世の中は否応なしに複雑化している。少なくとも、複雑な状況にある領域にも関わらず、分析に時間をかけて機能要件を網羅するようなソフトウェアプロダクト開発は、無秩序な領域にはまり込んでいると言わざるを得ないだろう。

note.com

開発組織を分散モノリスにしないチーム分割と協働のデザイン

複数チームに分かれたプロダクト開発スタイルをかえって不自由に感じてはいないだろうか。チーム間に張り巡らされた無数のチェーンが自由を奪い、チームの活動を束縛する。そんな感覚だ。

組織を複数のプロダクト開発チームに分割する組織アーキテクチャは、マイクロサービスアーキテクチャに例えることができる。そこから見いだせる原則は、チームをコンポーネントとして捉え、凝集度を高く、結合度を低く設計することだ。この原則を軽視すると、チーム間の依存関係が互いをチェーンのように繋ぎ、絡み合い、組織全体を「分散されたモノリス」に変えてしまう。その結果として、チームは日々、多大な調整コストや遅延コストを支払い続ける羽目になる。

では、既存のソフトウェアプロダクト開発において、個々のチームが活動しやすい分散型組織の設計とはどういうものだろうか。あくまでも私の経験や考えに基づくものではあるが、本稿はこれをテーマに書いてみたい。

「分散されたモノリス」と化したプロダクト開発組織

チーム間を縛りつける制約というチェーン

凝集度(cohesion)」と「結合度(coupling)」は、ソフトウェアコンポーネントモジュール性(modularity)の程度を示す尺度だ。 凝集度が高いほど、また、結合度が低いほど良いとされる。

凝集度が高いコンポーネントは、それが提供しようとする特定の機能に対し、関係性の高い責務を網羅的に含み、関係性の低い責務をほとんど(あるいは全く)含まない。凝集度が高いと結合度が低く、凝集度が低いと結合度が高い傾向がある。

チームとは、組織を構成するコンポーネントだ。ソフトウェアコンポーネントと同様に、チームにも凝集度と結合度がある。そして、チーム間の結合は、チームの活動に制約を課す。

チーム間の結合によってチーム活動に課される制約
チーム間の結合によってチーム活動に課される制約

本来、デリバリパフォーマンス上で好ましい組織の状態とは、チーム間が疎結合であり、それぞれのチームが独立してミッション遂行が可能であることだろう。チームが何かをしようとするたびに、他のチームに何らかの協力を依頼せざるを得ないようでは、高いパフォーマンスは期待できない。

しかし、結合による制約は、チーム間をチェーンで繋ぐかのように、互いの活動に干渉する。これらがクリティカルチェーンクリティカルパスを形成し、ソフトウェアデリバリのリードタイム(lead time for changes)を悪化させる。それはまるで、互いが無数のチェーンで繋がれた「分散されたモノリス(distributed monolith)」のようだ。

「分散されたモノリス」と化したプロダクト開発組織
「分散されたモノリス」と化したプロダクト開発組織

本来、「分散されたモノリス」という言葉は、サービス間が密結合になってしまったマイクロサービスアーキテクチャを指すアンチパターンの名だ。 しかしながら、組織設計のアンチパターンとしても、何ともしっくりくるではないか。

制約を生じさせる代表的な三つの結合要因

制約というチェーンを全て完全に断ち切ることは、おそらくできないだろう。しかし、その数を削減したり、緩和することはできる。そのためには、分散組織の凝集度を可能な限り高く、結合度を可能な限り低くなるよう、デザインする必要がある。

自組織の凝集度・結合度のレベルは、二つ以上のチームが参加するミーティングの実施頻度で把握できる。そこで話し合われているのは、互いのチーム活動に干渉し合う制約に対する調整だ。このような調整が頻繁に行われる組織は、チーム間の結合度が高い。

その調整のテーマは概ね次の三つのいずれかに大別できるのではないか。

  • 複数チームによる人員の共有
  • 複数チームによる同一案件の実施
  • 複数チームによる同一コンポーネントへの変更

ソフトウェア開発の現場でよく見られるこれらの行為が、なぜチーム間の結合要因となるのだろうか。そこで生じる制約とはどういうものなのだろうか。

「複数チームによる人員の共有」による制約

チーム間での人員の共有、すなわち「兼務」や「兼任」と呼ばれるそれは、チーム間の結合度を高める要因となる。人的リソースの利用を接点として、チームとチームが互いのスケジュールに干渉し合うからだ。

「複数チームによる人員の共有」による制約
「複数チームによる人員の共有」による制約

二つ以上のチームが、ひとりの人員に対し、同時期に作業をスケジューリングしようとすると競合が起きる。その解決方法は二つ。ひとつは優先順位を決めて順に作業を終わらせること。もうひとつは複数の作業を同時に進めること。

前者の直列方式では、優先順位が低いとされた作業を抱えるチームが、先行の作業が終わるまで待たなければならなくなる。後者の並列方式では、マルチタスキングによって、いずれのチームの作業も、実行期間が長くなる。どちらにしても、結局はリードタイムを悪化させることになる。

兼務の問題は以前の記事「兼務はチームの独立性とのトレードオフ」で詳しく書いたので、そちらも参照してほしい。

mtx2s.hatenablog.com

「複数チームによる同一案件の実施」による制約

機能追加や機能改善といった開発案件をチームが進める上で、必要となる責務をチームが網羅できていない場合、欠けた責務を担う他のチームに協力を仰ぐことになる。つまり、チーム機能の凝集度が低いのだ。

ひとつの開発案件を複数のチームで協力して進めるには、互いのスケジュールを調整する必要がある。しかし、協力を依頼されたチームも手が空いているわけではなく、常に様々な開発案件を抱えている。そのため、依頼先チームでの作業スケジューリングが、依頼元チームの期待通りになるとは限らない。結果、このような協力関係は多くの場合、リードタイムを悪化させることになる。

「複数チームによる同一案件の実施」による制約
「複数チームによる同一案件の実施」による制約

この問題は、「バリューストリーム」という観点で別の記事「マルチバリューストリームというアンチパターン」でも取り上げたので、そちらも参照してほしい。

mtx2s.hatenablog.com

「複数チームによる同一コンポーネントへの変更」による制約

二つ以上のチームがそれぞれの開発案件の都合で、同時期に、同じコンポーネントに対して変更を加えようとすることがある。これが、チーム間の結合度を上げる。そうすると、変更しようとするコンポーネントに関するテストやデプロイの実施日について、チーム間での調整が必要となる。

「複数チームによる単一コンポーネントへの変更」による制約
「複数チームによる単一コンポーネントへの変更」による制約

テストやデプロイの実施を、チームを跨いで統合実施しようとすると、その実施タイミングはもっとも遅いチームに合わせることになる。逆に、テストやデプロイの実施をチーム間で優先順位を決めて順に実施しようとしても、優先順位が低いチームは先行チームの実施完了を待つことになる。いずれにしても、リードタイムを悪化させてしまう。

結合要因の排除

以上のように、三ついずれの結合要因においても、それぞれの制約によって、ソフトウェアデリバリのリードタイムを悪化させるに至っている。この制約が、冒頭で述べた「チームの活動を束縛する」という感覚を生じさせているのだろう。

本稿は、この三つの結合要因を可能な限り排除し、制約を削減・緩和することを戦術として、以下に具体的な施策を講じている。

チーム分割と協働のデザイン

チームというアトミックで不可分な単位としての活動

チーム内で複数の開発案件が並列で走ることはあり得る。しかし、マネジメント主体がそれぞれの開発案件ごとに分かれているなら、それはもはやチームとしての体を成していない。

こういうケースでは往々にして、個々の開発案件ごとにプロジェクト体制が敷かれ、専任のプロジェクトマネージャーが立ち、企画担当も含め、いくつかのチーム・組織から人が集められる。チームの存在は、都度発生するプロジェクトに人的リソースを提供するリソースプールでしかない。

リソースプールとしてのチーム
リソースプールとしてのチーム

このような体制に関する問題は、note に書いた記事「ソフトウェアプロダクトの機能追加・機能改善をプロジェクト体制で進めるのはもうやめよう」でも扱った。

note.com

そこでも書いた通り、プロジェクト体制は、兼務体制を生みやすい。 つまり、「複数チーム(プロジェクト)による人員の共有」による制約を受けやすくなる。

また、「複数チーム(プロジェクト)による同一コンポーネントへの変更」による制約も受けることになる。

複数プロジェクトによる同一コンポーネントへの変更
複数プロジェクトによる同一コンポーネントへの変更

だからこそ、プロダクト開発を担うチームは、アトミックでありたい。チームメンバーが個々に活動するのではなく、チームとして活動するということだ。チームは不可分な存在であり、チームとして開発案件の実行に責任を持つ。複数の開発案件が並走したとしても、チームとしてそれらをまとめてマネジメントする。これが、分散組織をデザインする上での核となる基本原則だ。

アトミックな単位としてのチーム
アトミックな単位としてのチーム

単一の担当チームによるコンポーネントの開発・保守・運用

ソフトウェアプロダクトは、複数のコンポーネントの集合体として存在するものだ。ここでは、このコンポーネントというものの粒度を、「何らかのプラットフォーム(動作環境)にデプロイする単位」としよう。この定義は、『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 字型人材」の集まりとしてチームが構成されていれば良い。

プラットフォーム別アプリケーションチームとプラットフォーム間移植

モバイルアプリを自社ソフトウェアプロダクトとして展開する組織は、iOSAndroid、それぞれのプラットフォーム向けにアプリを開発していることが多い。このようなプラットフォーム別に存在するコンポーネントの担当を、「アプリチーム」といった形でひとつのチームに統合していないだろうか。

確かに、同一ドメインを扱うという点では悪くはないが、互いのコンポーネントの間には、クライアントサイド/サーバサイドのような依存関係はほとんどない。つまり、プラットフォームごとにチームが分かれていても、制約は生じにくいのだ。これは、デスクトップアプリの Mac 版と Windows 版や、ウェブアプリケーションの PC 版とスマホ版についても言える。

それならば、プラットフォームごとにチームを分けた方が、チームとしての小回りが効く。

プラットフォーム別アプリケーションチーム
プラットフォーム別アプリケーションチーム

もちろん、組織には人員数という制約条件があるので、無尽蔵にチームを作れる訳ではないが、可能な範囲で分けておくことをおすすめする。なぜなら、プラットフォームごとにチームを分けた体制は、互いに異なる機能を開発し、互いにそれらを移植するという、「たすきがけ」のような開発をやりやすくするからだ。同時に同じ機能を開発するより、いずれかのプラットフォームで作り上げた機能を、別のプラットフォームに移植する方が開発効率が良いはずだ。

プラットフォーム間でのたすきがけ開発と移植
プラットフォーム間でのたすきがけ開発と移植

それぞれのプラットフォームで同じ機能開発を進めることも可能だが、仮説検証を重視する昨今の開発スタイルに合わない。想定通りの価値を生み出せるかどうかわからない機能を、複数プラットフォームで同時に開発することは非効率だ。単一のプラットフォームで検証を繰り返し、機能を適度に磨き込んでから、別のプラットフォームに移植することで、無駄な開発を避けることもできる。

ただし、クリスマス向け機能といった、期限が重要な意味を持つ機能開発については、複数プラットフォーム同時で開発することになるだろう。

プラットフォーム別アプリケーションチームにおける垂直統合

ここで疑問がひとつ起きる。クライアントサイドのコンポーネントはプラットフォームごとに分かれているが、サーバサイドのコンポーネントは共有しているというケースでは、どのような分散組織をデザインすれば良いかだ。

このようなケースではまず、プラットフォームごとにクライアントサイドコンポーネントを担当するチームを分ける。その上で、サーバサイドのコンポーネントは、そのいずれかひとつのチームに垂直統合する。そして、サーバサイドのコンポーネントを担当しないチームは、必要に応じて、コントリビューター/コミッターモデルでサーバサイドコンポーネントに変更を加える。

プラットフォーム別アプリケーションチームによる垂直統合
プラットフォーム別アプリケーションチームによる垂直統合

負荷の高いチームへのコントリビュート型ヘルプ体制

あるチームが人手不足に陥った時、他のチームから、人手が貸し出されていないだろうか。このやり方は、「複数チームによる人員の共有」に該当してしまう。

とは言え、人手不足で困っているチームを放置するわけにはいかない。ここは考え方を変えてみよう。困っているチームに人を派遣するのではなく、困っているチームのタスクを切り出して、他のチームがそれを請け負うようにすれば良い。もちろん、請け負ったチームは、コントリビューターとして協働するのだ。

コントリビュート型ヘルプ体制
コントリビュート型ヘルプ体制

最後に - 組織は戦略に従い、システムは組織に従う

「うちの会社は組織を頻繁に変える」という愚痴を耳にすることがある。変化というのは受け入れられにくいものだが、そのような言葉が多く出てしまうようなら、組織を変化さる意図を、社員に上手く伝えられていないのだろう。

組織は戦略に従う(アルフレッド・チャンドラー)」と言うように、組織を変える理由は、戦略を変えることにある。組織を頻繁に変えると言うのは、見方を変えれば、ビジネスを取り巻く内外の状況変化に対する組織の適応力が高いのだ。

プロダクト開発チームも組織の一部だ。ビジネス戦略の変化に応じて、プロダクト開発組織としての変化が求められる。

ではその戦略に従って、チームの分け方を柔軟に変えられるかというと、それは難しい。本稿で話してきた通り、既存のプロダクトのコンポーネントのあり方が、チームの分け方に強く影響を及ぼすからだ。戦略に合わせて無理な変化を加えてしまうと、そこで生じた制約によって、組織が「分散されたモノリス」になってしまう。

このことから、戦略に従って形作られたビジネス組織全体と、コンポーネントの構成に従って形作られたプロダクト開発組織は、ねじれが生じやすい。そしてこのねじれは、ビジネスパフォーマンスを阻害する要因になり得る。

ジレンマだ。ねじれが無いようプロダクト開発組織を編成すると、制約によってデリバリパフォーマンスを落とし、ねじれを受け入れるとビジネスパフォーマンスを落とす。

私はやはり、制約によるデリバリパフォーマンス悪化は避けるべきものだと考えている。制約を受け入れてしまうと、デリバリパフォーマンスの悪化がビジネスパフォーマンスを悪化させる。そのことは、書籍『LeanとDevOpsの科学』の調査からも明らかだろう。

制約を受けながらもビジネスパフォーマンスに影響を与えない方法があれば良いのだが、少なくとも現時点ではその妙案が、私には考えつかない。だから、デリバリパフォーマンスを最大化するプロダクト開発組織を設計することに注力する。その上で、そこで生じたねじれに対して、短期の対策と中長期の対策を講じる。

短期的には、戦略に従った組織との接点に、ねじれによるビジネスパフォーマンス低下を解消(あるいは緩和)する構造を設ける。簡単に言えば、戦略に基づくビジネス組織の動きを、プロダクト組織へ迅速に伝搬させ、連動させることを可能にする。これについてはまだ試行錯誤中なので、ここでは割愛する。

中長期の対策については、「組織は戦略に従い、システムは組織に従う」という状態を実現することだ。そうすればねじれも制約も避けることができる。

「システムは組織に従う」と言えば、「コンウェイ戦略(Inverse Conway Maneuver)」を思い出す。しかし、既存のソフトウェアシステムに対し、その構造に関係ない組織を組んでしまうと、先述の通り制約によってデリバリパフォーマンスが悪化しかねない。その上、システムアーキテクチャが組織アーキテクチャに追いついたころには、ビジネス戦略が変わって組織も変わってしまっている。それほどまでに、ビジネスを取り巻く外部環境の変化はめまぐるしい。

だから我々にできることは、個々のコンポーネントの責務を小さく、そして凝集度を高く、結合度を低くすることで、プロダクト開発組織の分割と、担当コンポーネントの編成を柔軟に組み替えやすくすることではないか。日々の地道な蒸留を続けていくことが、その実現の唯一の方法だろう。

開発チームは本当の顧客視点を持っているか

自社ソフトウェアプロダクトのひとつが軽微な障害を起こした。

問題のコンポーネントを担当する開発チームによる一次対応がようやく落ち着き、一息ついたところで、チームリーダーから、障害に関する詳細な報告をZoom越しに受けていた。直前までリモート会議続きだった私は、この時点ではまだ、障害の内容を把握できてなかった。こういう時、流れ切ったSlackチャンネルを後から見て、情報を正確に追いかけるのはなかなか難しい。

バックエンドAPIが、本来であれば処理可能なリクエストに対し、特定の条件下で誤って 400 Bad Request を返していたという。リーダーは、一通りの報告が終わったところで、「何か不明点などありませんか」と私に尋ねた。

この問題の影響を受けたリクエストの件数や期間、APIの振る舞いや内部処理の問題点についてはよくわかった。しかし、その外側がわからない。ステータスコード 400 を返されたことによって、APIクライアントであるスマートフォンアプリは、どのような振る舞いをしたのだろうか。その結果、アプリを利用するユーザーには、どのような影響があったのだろうか。

「アプリ側の実装によるので、アプリチームに聞かなければわかりません」と、これが、リーダーの答えだった。

アーキテクチャの観点で言えば、その通りだろう。バックエンドAPIの仕様、それが、アプリとバックエンドの間での合意事項だ。アプリがどのようにAPIを呼び出しているのか、バックエンドがAPIをどう実装しているのか、互いにそれらに依存すべきではない。こうすることで、アプリ、バックエンドそれぞれのデプロイ容易性を上げ、デリバリパフォーマンスを高く維持しているのだ。

しかしそれは、バックエンドチームがアプリやユーザーについて知らなくて良い、ということではない。社外に公開する汎用的なAPIを提供しているわけではなく、サービスクライアントとしては専用のiOSアプリとAndroidアプリがあるだけだ。アプリのこと、その先のユーザーのことを知らなくて、良いサービスを提供できるとは思えない。そもそも、障害によってユーザーにどのような影響を与えてしまったのか、気にならないのだろうか。

――今の開発チームの目には、ソフトウェアシステムしか映っていないのかもしれない。

そのような思考を巡らす中で、私は、以前の職場でのエピソードを思い出した。

数年前、いや10年近く前だったかもしれないが――ある日の午後、企業向けSaaSプロダクト開発の責任者だった私のデスクに、紺のスーツ姿の男性が近づいてきた。当時はまだ、オフィスに集まって働くスタイルが一般的だった。私のデスクの周辺は、カジュアルな服装のエンジニアばかりが集まるエリアだ。近づいてくるのが営業部員であることはひと目でわかる。

「お疲れさまです。少し、お時間よろしいですか」と、落ち着いた口調でそう言うと、私を促すようにして、すぐ近くのミーティングルームに向かって歩き出した。

4人程度が座れる小さなミーティングルーム。テーブルを挟んで二人で向かい合って座り、彼が口を開くのをまっていた。彼と話す機会はこれまでほとんどなかったが、営業成績がナンバーワンの若手エースだったので、顔はよく知っている。

話は察しがつく。ちょうど今、開発チームが、プロダクトの障害対応にあたっている。多くのクライアントを持つプロダクトだ。彼が担当する顧客にも、おそらく影響があったに違いない。プロダクト開発の責任者である私に対し、障害対応の状況を聞きに来たのか、あるいはクレームを伝えに来たのだろう。後者だとしたら、珍しいことではあるが。

「今日開かれたA社のキャンペーンイベントは、この障害で台無しになりました」と、感情の読み取りづらい視線を私に向けた。抑制されたトーンで話してはいるが、言葉の選び方からは、怒りが滲み出ている。

障害が発生すると、営業部員は、担当する顧客からの直接の問い合わせや苦情の電話に応えたり、場合によっては、客先へ謝罪に向かうなど対応に終われる。当然ながら、本来の営業活動にあてる時間は奪われてしまう。しかし、そんな矢面に立つ中でも彼らは、私たちに文句を言うわけでもなく、むしろ「これも自分たちの仕事です。開発のみなさんは、良いプロダクトを作ることに集中してください」と言う。

しかし、ここのところ立て続けに障害を起こしてしまっている。

執務室では、声を掛け合いながら慌ただしく対応に追われるエンジニアの姿が、ここからでも壁面のガラス越しによく見える。障害を起こしたのは、企業のキャンペーンイベントでよく利用される機能だった。これまで10年以上、改修を繰り返してきた機能で、ローンチ当初から複雑だったコードは今やカオスとなり、保守が困難な状況にまで至っていた。変更を加えると多くの不具合を混入しやすい箇所であり、数日前にも軽微な問題を起こしたところだ。確かその際にも、彼が担当する別のクライアントが被害を受けていたはずだ。

「先方の担当者のBさんは、このイベントの準備に本気で取り組んでおられました」と、こちらを向いて、ゆっくり話しはじめた。「お客様の思い出になるイベントにしたい、そう、おっしゃっていて、だから、僕も、我々のプロダクトを活用したイベントが成功するよう、Bさんに全力で協力して……」

沈黙。

「くやしいです、イベントを楽しみにされていたA社のお客さまにも申し訳なくて……でも開発の人はいつも、そういったことを気にかけている様子も見えなくて、本当に、このプロダクトをお客様にすすめても良いのか、わからなく……」

そこまで話して、うつむいて泣きだした。その理由が、売り上げや成績の心配ではなく、顧客のこと、その先のエンドユーザーのことを想ってのことだと伝わってくる。彼が、ナンバーワンである理由がわかった気がした。

ソフトウェアプロダクト開発を担うエンジニアが、顧客と向き合う機会は多くない。企業向けプロダクトならまだしも、コンシューマー向けならなおさらだろう。まして、企画段階にも参画しないなら、顧客体験について思いを巡らす機会はほとんどない。エンジニアにとっての顧客とは、アプリケーションが吐き出すログや、モニタリングダッシュボード上のグラフや数字でしかないのかもしれない。これでは、エンジニアの目に、ソフトウェアシステムしか映らなくなってしまうのも当然だろう。

当時の私は、その出来事から以降、プロダクト企画部門も巻き込んで、開発プロセスもシステムも組織も大きく変えていった。顧客体験を中心に据える開発組織を作ろう、と。そして、開発チームからの障害報告にはまず、「お客さまへの影響は?」と問うことにしている。

あれから何年も経ち、いま振り返ってみて、目指す組織を作り上げられたのかと自分に問うと、自信を持って答えることはできないように思う。今はそこから離れ、こうしてまた開発チームのリーダーから障害報告を受けている。Zoom越しに映し出されたGrafanaのカラフルなグラフを眺めながら、またあそこからはじめてみようと思った。