Control Plane部の小森 (@littleforest12)です。 こちらはキャディ株式会社のアドベントカレンダーの16日目の記事です。
最近、社内でこれまでの中では比較的大規模な開発プロジェクトのリードアーキテクトを拝命しまして、奔走しています。 プロジェクトはようやく立ち上がってきたところですが、アーキテクトとしての考えをメンバー向けに発信しようと書きはじめたことを、せっかくなのでTech Blogにしたためたいと思います。
背景
私たちは「製造業AIデータプラットフォーム CADDi」としてプロダクトを展開しており、プラットフォームの一部としてCADDi DrawerやCADDi Quoteといったアプリケーションを提供しています。 もちろん、これらは開発から運用まですべてが内製です。 通常は、各アプリケーションやプラットフォーム横断で、おおむね4〜8名程度のチームに分かれて新機能の開発や改修といった開発プロジェクトを日々進めています。
今回は複数チームの担当領域にまたがる開発が必要で、チーム横断でのアーキテクチャ設計や課題解決が必要となりました。 そこで、初期段階から関わっていた私がリードアーキテクトを担当することになりました。
私自身はいつもアーキテクトをやっているわけではありません。 ソフトウェアエンジニアとして、状況に応じた役割を担っていますが、今回は久しぶりに(そしてキャディにジョインして初めて!)アーキテクトの帽子をかぶることになりました。 これまでも、開発現場でアーキテクトやそれに準ずる立ち位置で仕事をしたことは何度かあります。 そのたびに悩んでいたのが「自分が考えたアーキテクチャを、どうやってみんなに受け入れてもらうか?」ということです。 何度かこの立場を経験することで、自分なりのアプローチが見えてきました。
本記事の前半ではこの悩みの背景を説明し、後半では私がとっているアプローチを紹介します。 先に核心を知りたい方は「腹をくくり、最後まで伴走しきってこそ、アーキテクト」から読みはじめてください。
目次
アーキテクトはすべてを見通せない
アーキテクチャとは、「要求事項をシステムとして実現する際の、その方式の屋台骨」とも言えるわけですが、最初に考えたアーキテクチャがすべての状況を想定できるわけではありません。 私たちが未来を見通せない以上、どんなアーキテクチャも、初期段階では本質的に不確実です。 この不確実性には、2種類の要因があります。 それは、「要求/要件レベルの不確実性」と「技術レベルの不確実性」です。 アーキテクチャ策定をすべきプロジェクトの初期段階では、この両方の不確実さを受け入れねばなりません。
要求/要件レベル(What)の不確実性
多くの皆さんがご存じの通り、ITシステムやプロダクトに対するユーザーの要求をきちんと言語化するのは困難です。 「ユーザーは何がしたいのか、そのためにシステムは何を実現すべきなのか」というWhatがはっきりわからない、というのが「要求/要件レベルの不確実性」です。 ウォーターフォール開発の考え方では、要求や要件を明確にしてからアーキテクチャを考えるべきですが、多くのユーザーにサービスを提供するプロダクト開発では、アーキテクチャ策定段階ですべての要求/要件を明確にするのは不可能です。
また、プロダクトは常に進化していくものです。 うっすらと見えている近い将来の構想に備えた「拡張性」という非機能要件も求められますが、うっすらとしか見えていないため「拡張性」という要件の具体的な言語化が困難で、当然それを踏まえたアーキテクチャ設計も困難になります*1。
技術レベル(How)の不確実性
一方、要求/要件が確実であっても、その実現手段(How)がはっきりしないというのが「技術レベルの不確実性」です。
たとえば、典型的な要件で類似の開発事例も多いのなら、技術的な不確実性はほとんど無いでしょう。 古い例ですが「Web-App-RDBの三層構造で、SpringFrameworkを使えばOK!」程度の決めでも、なんとかなります。
こういった方針を決めておき、「残りは開発チームの皆さんでよろしく!」と、次の現場へ颯爽と去って行くアーキテクトはカッコいいかもしれません。 しかし、そんな理想通りにコトが進む開発現場を、私は見たことがありません。
なにがうまくいかないのでしょうか?
初期段階で、すべてを見通せるわけがないからです。 特に、多くの顧客に対して提供されて運用中のサービスに対する広範囲の修正や機能追加は、技術的に大きな不確実性を伴います。 残念ながら、既存システムの設計や振る舞いが、すべてきちんとドキュメント化されていることは稀でしょう。 そのため、アーキテクチャ策定段階で、それ以降の設計レベルの整合性を確認しきるのが困難です。 また設計が進んでから、隠れていた要件が発覚することも多いでしょう。 見落としていた要件に対応するため、アーキテクチャの見直しが必要になるケースもあるかもしれません。
アーキテクチャや基本設計レベルでは整合性が取れているように見えても、より詳細な設計や実装を進めていったときにはじめて明らかになる不整合はどうしても発生します。 たとえば、次のような状況を経験した人は多いのではないでしょうか。
- 機能Aの振る舞いを変更した結果、本来予定していなかった機能Bの振る舞いも同じように変更しないと、プロダクトとしての整合性がとれなくなる
- 新しい責務をコンポーネントAに追加したが、よくよく考えを進めると、その責務を持つべきなのはコンポーネントBの方だった
チームの責任範囲に閉じた不整合ならば、チーム内の設計変更で済むので、大きな影響はありません。 しかし、機能配置や責務の見誤りといったレベルの問題は、チーム境界を跨がる問題に発展します。 本来ならば、アーキテクチャのレベルで軌道修正するのが筋だったりするケースも多いでしょう。
さらに、ウォーターフォール開発で工程毎に担当会社が違うようなケースはもっと大変です。 手戻りのコミュニケーションコストの方が大きいので、釈然としない思いをしつつ、現場レベルでの辻褄合わせをせざるを得ないケースも出てきます。 (そして「なんでこれを見落としたんだ!」と、人知れずアーキテクトは恨みを買います!)
不確実性にどうアプローチするか?
この不確実性に対処するための一般的なアプローチはアジャイル開発です。 開発サイクルを高頻度にまわし、ユーザーからのフィードバックを受けることで不確実性から受ける影響を最小化するというアプローチを取ります。 小さな粒度のリリースを繰り返すため、アーキテクチャも漸進的に改善でき、技術的な不確実性も下げられるでしょう。 特に新規開発では、このアプローチはかなり有効です。
一方、アジャイル開発的アプローチが取りにくい状況もあります。 その1つが、既に多くのユーザーに使われているプロダクトに対して、既存機能に対する横断的な機能追加や変更を行う場合です。 現在私が取り組んでいるプロジェクトが、まさにその状況です。
私たちの展開するプロダクトのお客様は企業であり、場合によってはお客様側の業務にがっつり組み込まれて利用されているケースもあります。 そのため、利用者が増えれば増えるほど、すでに運用中の機能全体に影響するような変更を、そう簡単にリリースできなくなります。 本来ならば、ベータ版などを提供して反応を見ながら改善するといった手法をとりたいところです。 単発の新機能であればそれがしやすいですが、横断的な機能ではそうもいきません*2。 下手をすると、お客様の企業活動に影響を与えるレベルの障害を発生させかねないためです。
腹をくくり、最後まで伴走しきってこそ、アーキテクト
ここからが本題です。 このようにさまざまな不確実性がある状況で、プロジェクトを前に進めるために、アーキテクトはどのように振る舞うべきなのでしょうか。
最初に考えた案通りに最後まで走りきれることはむしろ少ないにも関わらず、初期段階では、多くの選択肢のうち、正解が明らかな状況は希です。
アーキテクトは無数の選択肢から、整合性をもって目的を達成できる組合せを複数生み出し、そこからさらにより良いと信じられる選択肢を選び取って最後は「それに賭ける!」と腹をくくらなければなりません。 まさに、目をつぶって針の穴に糸を通すような困難なことです。
私はアーキテクトを担当する以上、「設計・実装・テスト・リリースまで開発プロジェクトに伴走し、初期に考案したアーキテクチャが実効性を持って形となるまで責任を持つべき」だと考えています。 その過程では、チーム間での仕様を調整したり、全体に関わる課題の抽出や解決といった活動を継続的に行わなければなりません。
アーキテクトはスーパーマンではない
とはいっても、アーキテクトもただの人間です。スーパーマンではありません。 プロジェクト規模が大きくなると細部すべてに目を配れませんし、責任感が勝ってすべての問題を一人で解決しようとすると、自分自身がボトルネックとなってプロジェクトを止めてしまいます。 「自分があと10人いれば・・・」なんて思ったことがある人も多いでしょう。私もよく思います。
では、どうすればよいのでしょうか。 私がこれまでの経験から得たポイントは、次の3つです。
- 考えの道筋を共有すること
- 細部の検討は各チームやメンバーに委譲すること
- 自分の考えにこだわりすぎず、変更を許容すること
それぞれ説明しましょう。
1. アーキテクチャに至った考えの道筋を共有する
自分一人ではすべてを解決できないので、できるだけ同じ考えを共有し、同じ考えに基づいて解決できる人を増やす作戦です。 いわば、アーキテクチャ思考の水平スケーリングです。
今回は、初期アーキテクチャを考えるにあたり、プロジェクトに関わる各チームの主要人物に協力してもらい、アーキテクチャ検討チームとして一緒に考えました。 既存機能については、当然各チームのメンバーの方が詳しいですし、もとより私も今のシステムを細部まで把握できているわけではありません。 それぞれから設計案を出してもらいつつ、議論を重ねて私がそれを取りまとめるやりかたにしました。
アーキテクチャ検討チームとしてのアウトプットは、以下のようなものになりました。
- 全体の概念モデル
- 要求に対する理解を図示し、要件へと落とし込むための入り口となるもの。
- ER図として表現。
- ざっくりアーキテクチャ
- 各チームが担当する既存コンポーネントに対する責務分担
- おおまかなレベルのAPIや想定シーケンスの提示
- 検討過程で抽出した技術課題の一覧・要件の一覧
- アーキテクチャ策定過程で解決したものもあるし、各チームに解決を委ねたものもある
- 課題によっては「○○チームと××チームの間で解決してください」と委ねるケースもある
- 要件はPdMと話して「やらない」と決めたことやその理由も明らかにする
検討過程では、PdM(プロダクトマネージャー)が提示した要求事項を、PdMとの議論を重ねて具体的に深掘りしたり、不明確だった要件の具体化といった作業も行いました。
コミュニケーション量はかなり多くなりましたが、議論を経ることでアーキテクチャ検討チーム内の考え方は自然と揃ってきます。 この時点で、同じくらいの解像度でアーキテクチャを理解している人が複数できるので、一人でアーキテクチャを考えてドキュメントだけを通して伝えるよりも、後々の効率が格段に良くなります。
「アーキテクチャ設計」に対するアウトプットイメージは人によって違うかもしれませんが、ここまでのアウトプットをドキュメント化すれば、「アーキテクチャ設計完了」と言えるかもしれません。 しかしこのままでは、これ以降明らかになるであろう、さまざまな不確実性には対処できません。
ここまでは下準備。「アーキテクチャ案」と呼べるのがせいぜいで、これから先が本番です。
2. 各チーム/メンバーに細部の検討を委譲する
ここまでのアウトプットを元にして、「1Day camp」と称した開発メンバー全員が集まる場を設定し、理解度のさらなる平準化を図りました。 午前中は概念モデルやアーキテクチャの解説、午後は実際の既存画面を全員で確認しながらPdMも交えて細部の要件の確認や議論を行いました。 特に午後のパートはアーキテクチャを踏まえた上で、「システムとしてユーザーにどのような振る舞いを見せるか?」「その振る舞いは今想定しているアーキテクチャで実現できるのか?」といった実効性のある議論ができました。
半日ですべての課題を解決はできませんが、「何が課題なのか?」「課題を解く前提とすべきアーキテクチャはどんなものか?」という目線が揃えられれば、大きな前進です。 それ以降の検討は、関係するチームに委譲することができます。 実際、この「1Day camp」を経ることで、各チームに細部の検討を委譲できるようになり、並列度があがって一気にスピードが増しました。
もちろん、アーキテクトとしての私は、可能な限り各チームの議論に参加したり、検討結果をチェックしたりして、当初の思想から大きなズレが生じないようなコントロールをしていきます。
3. トップダウンで押しつけない・こだわらずに提案を許容する
そして大事なのが、初期のアーキテクチャにこだわりすぎないことです。 委譲した先で冒頭でも説明した整合性や要件の見落としなどが発覚すると、初期のアーキテクチャ案の見直しを迫られることもあるでしょう。 仮に「アーキテクチャ」という成果物「だけ」を受け取ったエンジニアからすると、その考え方に十分同意できずに「こんなアーキテクチャはイケてない!」と思ってしまったり、「自分だったらこうする!」と、別の方向性を思いついたりする人もいるはずです。
アーキテクトにとって、せっかく考えたアーキテクチャ通りに開発が進まないのは、あまり気分が良くないものかもしれません。 せっかく時間をかけて決めたことが蒸し返されるのは、非効率なコミュニケーションに見えることもあります。 それに対してアーキテクトが「いやいや、とにかく既に決めた事だから、この方針で作ってくれ!」という意志決定を押し通すと、どうなるでしょうか。 短期的にはコミュニケーションコストをかけずに済むので、良いかもしれません。
しかし、長期的には各エンジニアがそのプロジェクトでこの先発生する課題を自力で解決できず、どこかでアーキテクトにそのしわ寄せが来ます。 もし、その時点でアーキテクトがプロジェクトから離れていたら、当初のアーキテクチャは結局崩壊するでしょう。 つまり、不確実性の高いアーキテクチャを強制するのは結局失敗するのです。
現代のコンピュータシステムは、最終的にすべての振る舞いをコードで表現しなければ、期待通りに動作しません。 アーキテクチャや基本設計の段階で細部に至るまですべての振る舞いを規定できない以上、最終的には細部の設計を考えたりコードを書いたりする人の裁量に委ねなくてはならない部分がでてきます。 アーキテクチャなどの方針に納得できていない状況で、アーキテクチャに起因する想定の課題に直面したとき、エンジニアは自分の考えでそれを解決するのが困難になります。少なくとも、圧倒的にペースは落ちるでしょう。 開発が進んだ状況でこうなってしまうと、リカバリは困難です。
ですので、自分の考えとは違う設計案がメンバーから提示されたとき、明らかな誤りがないのであれば、私は一旦はそれを受け入れ、その人に引き続きその案を主体的に検討することをお願いするようにしています。 結果、その人が提示した案が採用され、より良い設計とできれば互いにとってHappyですし、最終的に提示された案がボツとなって当初の案に戻ったとしても、無駄ではありません。 その人は自分自身の案を考え、関係者と議論した結果、元の案に落ち着いたので、納得度が違います。 「与えられた方針の通りにつくる」のではなく、「自分で考えて至った結論に基づいてつくる」ので、以降の工程で発生する予期せぬ課題に対しても、より自律的に解決のための行動を取ることができるでしょう。 このようなやり方は、目先は無駄なコミュニケーションを取っているように見えます。 一見遠回りに見えますが、このほうが結果的にプロジェクト全体への良い影響(レバレッジ)が大きくなるのです。
このため、「アーキテクトが初期の設計にこだわらず、メンバーに細部の検討を委譲しつつも一緒に考える」ことで、プロジェクトメンバーとの目線・コンテキストを揃えることができ、アーキテクトがボトルネックとなることを防げます。
「共創」で不確実性を乗り越える
アーキテクトが以上のような行動を取ることで、「アーキテクトが考えたアーキテクチャ」ではなく、「みんなで考えたアーキテクチャ」となることを私は期待しています。 アーキテクトが最初に提示するのは、その叩き台にすぎません。
さて今回、このアプローチがうまくいくのかどうか。 これは私にとっても挑戦であり、結果がわかるのは、しばらく先です。
最後に
キャディでは、一緒に働く仲間を募集中です。 本記事で紹介したようなアプローチが取れるのも、それを受け入れて自律的に考え、行動に移せる頼もしい多くの仲間がいるからこそです。
そんなキャディでのソフトウェア開発に興味を持った方は、ぜひ採用サイトをご覧ください。 カジュアル面談もお待ちしています!
CADDi Careers - 採用総合サイト careers.caddi.com
CADDi Careers - エンジニア向け採用情報 careers.caddi.com