ソフトウェア設計思想「SOLID原則」を開発組織設計に適用したらどうなるか?

この記事はQiita Engineer Festa 2023参加記事です。

# はじめに

今年の４月よりリードエンジニアからエンジニアリングマネジャーになりました。
7年エンジニアをゴリゴリやって、
```
Android App Developer
↓
Web App Developer
↓
DevOps(Infra/CI/CD) Developer
↓
Scrum Master
↓
Cloud Solution Architect
```
などの領域を経験しました。
その中で様々なプロジェクトで開発サイド、ビジネスサイドにおいて色んな職種の人と一緒に仕事して、
それぞれの考え方や悩みを見て聞いてきました。
(自分も皆さんに大変お世話になって、おかげて成長してこられました。とても感謝です(❁´ω`❁)！)

今度はEMになって開発組織の成長に取り組むチャンスを頂き、責任をもって、皆さんが認める良い開発組織を作って行きたいと思います。

**「良い開発組織とは」** に対して会社の文化、携わる事業の内容、目標によって理想な姿が様々なので、正解はないと思いますが、色々模索中の中、ソフトウェア設計思想から、「生産性、メンテナンス性性、拡張性」を軸に考えたら、いい組織設計のヒントになるのではないかと思いました。

本記事は **ソフトウェア設計思想「SOLID原則」から開発組織設計に適用したらどうなるか？** とのお題でお話させて頂きたいと思います。

また、本記事は **「考え方」** を着眼点としているため、良し悪しとせず、「一つの考え方としてはありではないか？」との観点でご覧いただければ幸いです。

# この考え方に至った背景

ソフトウェア設計において、Clean Architectureの作者、Robert C. Martin氏により提唱されたオブジェクト指向設計に関する設計原則、 **「SOLID原則」** がよく知られています。

オブジェクト指向設計が「相互に作用するオブジェクトを組み合わせてプログラムを設計する」という概念を持ち、つまり我々人間が認知するあらゆる物事に対する様々な複雑な関係性をモデリングするとの目的で作られた設計手法です。

なので、**「組織」** をモデリングすることも可能だと思います。(Minecraftみたいな感じ？)

つまり、**「SOLID原則」** がオブジェクト指向設計によるモデリング化をうまくいくために考えられた原則なため、その原則を適用すると、**「良い組織」** の作りのヒントになるのでは？との考え方に至ったのです。

SOLID原則は合計下記5つとなります。

```
1、単一責任の原則（SRP）
2、オープン・クローズドの原則（OCP）
3、リスコフの置換原則（LIP）
4、インタフェース分離の原則（ISP）
5、依存関係逆転の原則（DIP）
```
それでは、本記事の本題、SOLID原則を組織設計に適用し、自分なりに解釈してみたいと思います。

# 1、単一責任の原則（SRP）

>There should never be more than one reason for a class to change.
(変更するための理由が、一つのクラスに対して一つ以上あってはならない）
>
>※引用先: https://ja.wikipedia.org/wiki/SOLID

単一責任という言葉を使われていたため、「一つのクラスが一つのことしかやらない」とよく誤解されるようですが、
ここで **「変更の理由は一つであるべき」** に焦点を当てることが重要で、
つまり、「該当クラスの責務変更には異なる役割を持つ役者によって変更されるべきではない」とのことですね。

それでいうと、組織もそうだと思います。

例えば、スクラムにおいては、プロダクトオーナー(PO)という役割を持つ人物が１人のみ存在します。

仮に2人がいるとすると、POのAさんとPOのBさんが優先に着手したいストーリーが異なる場合、開発現場が混乱してしまいます。

SRPの原則から考えると、**「与えられた責務範囲内の意思決定者を単一化する」** ことで意志決定内容のコンフリクトが回避されるので、コミュニケーションコストが下がり、組織の生産性向上を期待できると思います。

# 2、オープン・クローズドの原則（OCP）

>software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
（ソフトウェアの実体（クラス、モジュール、関数など）は、拡張に対して開かれているべきであり、修正に対して閉じていなければならない）
>
>※引用先: https://ja.wikipedia.org/wiki/SOLID

ソフトウェアの開発では、既存ソースコードの改修は完全新規コードの追加より大分コストがかかります。
なので、ソフトウェア設計時に、新規機能追加する際に、既存コードを変更せず(Close)に新たにコードを追加(Open)するのみで対応していける設計をすることで、コードの修正を最小限に抑え、そして最大限に拡張しやくようにできれば、開発コストの低減効果に繋がります。

組織で考えたら、事業予算や目標によって、組織体制が変わったりすることがよくあると思います。

例えば、予算増加で劇的な人数やチーム数の増加によって連携体制の変更などが例として挙げられます。

毎回なにか新しいことをやろうとするときに、既存の体制を変更しないと対応できない、もしくはうまく回らないことが発生したら、組織の生産性が落ちてしまいますね。

オープン・クローズドの原則から考えると、なにかの変化に対して、体制の基本構造を変更せず、ルールの追加、人員の追加、チームの追加だけで対応できる体制が素晴らしいと思いませんか？

例えば、一つの小さいサービスを開発、運用するのに最初に下記の組織体制とします。
(職域別でチームを分け、実際に開発をする際にProjectを立ち上げて必要な人員を集めて開発する体制です。)
![domain_a_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526011/9778be58-092d-9510-9334-b1b38c6069d4.jpeg)

プロジェクトがリソース不足で、人員を追加することになりました。
オープン・クローズドの原則を適用した場合、既存の体制を変更せずに人員を増やすとなると下記となります。
![domain_a_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526011/7416e838-1578-40ad-a3b3-c43acfd290f9.jpeg)

更に、ステークホルダーにより、ドメインAにおいて同時に2つのProjectをやれるようにしたい指示を受け、更に人員を増やしました。
オープン・クローズドの原則を適用した場合、既存の体制を変更せずに同時に２つのProjectをやれるだけの人員を増やすとなると下記となります。
![domain_a_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526011/73187a31-4d67-2835-e2c8-a3da5154f914.jpeg)

事業がとても順調で、ステークホルダーにより、ドメインBを立ち上げたい、且つ同時に２つのProjectをやれるようにしたい指示を受け、もっと多くの人員を増やしました。
オープン・クローズドの原則を適用した場合、既存の体制を変更せずに同時に２つドメインの２つのProjectをやれるだけの人員を増やすとなると下記となります。
![domain_a_b_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526011/700fbeb8-5f4b-74ef-9217-917dee93fb60.jpeg)


上記は人員増加による体制更新を行われましたが、どちらも体制の基本構造を変更していませんので、大きな体制を変更せずそのまま組織を拡張していけそうです。

ここでコンウェイの原則を思い出しますが、組織だけでなく、システムもドメインに応じて分割できると、とても高い組織パフォーマンスを期待できそうです。
(実際に現実世界はもっと複雑なので、ちょっと机上の空論っぽいかもしれませんが、組織の拡張に少しヒントになるかなと個人的に思います。)

# 3、リスコフの置換原則（LIP）

>Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
（ある基底クラスへのポインタないし参照を扱っている関数群は、その派生クラスのオブジェクトの詳細を知らなくても扱えるようにしなければならない）
>
>※引用先: https://ja.wikipedia.org/wiki/SOLID

この原則を正しく理解するのに結構難しいと思いますが、ここで深入りをせず、わかりやすく言うと、
サブタイプ（派生クラスやサブクラスや子クラス）がその基底型（基底クラスやスーパークラスや親クラス）の代替として振る舞えることを要求するということです。

下記のサンプルコードを見るとわかりやすいと思います。
```
class Animal {
    protected $name;

    public function setName($name) {
        $this->name = $name;
    }

    public function makeSound() {
        return "The animal makes a sound.";
    }
}

class Cat extends Animal {
    public function makeSound() {
        return "Meow!";
    }
}

function printAnimalSound(Animal $animal) {
    echo $animal->makeSound() . PHP_EOL;
}
```

ここでCatクラスは動物クラスを継承していて、AnimalクラスのmakeSound()関数を上書きをしています。
printAnimalSound(Animal $animal)関数が呼ばれるときに、
Catクラスのインスタンスを引数に渡しても正しく動作します。("Meow!"で出力される)

つまり、子クラスが親クラスの代替として振る舞えることができますね。

この原則が組織設計に重要なヒントは **「組織の一貫性がある」**　だと考えます。

例えば、会社では核心の価値観があって、それを体現するメンバーを集まることで、全体組織の力を最大化に図っていると思います。

弊社では **「うるるスピリット」** という核心的な価値観があります。それが上下関係なく、１人１人その価値観を認め、純度高く、体現しようとする姿勢があってこそ、組織が大きな力を出すことができます。

```
・うそをつかない、悪いことをしない
・会社はホーム、社員はファミリー
・相手の期待を超える「おもてなし」
・当事者意識を持って、納得して働く
・ベンチャースピリットを持ち、成長し続ける
```
引用先：　https://www.uluru.biz/our_policy

ここで実際に弊社の核心的な価値観「UluruSpirit」とチームの価値観「TeamSpirit」を設けて、リスコフの置換原則を適用した例を挙げてみます。

```
class UluruSpirit {
    public function embodySpirit() {    
        echo "うそをつかない、悪いことをしない。\n";
        echo "会社はホーム、社員はファミリー。\n";
        echo "相手の期待を超える「おもてなし」。\n";
        echo "当事者意識を持って、納得して働く。\n";
        echo "ベンチャースピリットを持ち、成長し続ける。\n";
    }
}

class TeamSpirit extends UluruSpirit {
    public function embodySpirit() {
        echo "挑戦的なプロダクト志向な精神を持とう！！\n";
    }
}

function printSpirit(UluruSpirit $spirit) {
    $spirit->embodySpirit();
}

$uluruSpirit = new UluruSpirit();
$teamSpirit = new TeamSpirit();

printSpirit($uluruSpirit);
printSpirit($teamSpirit);
```

我々のチームが作ったチームスピリットがうるるスピリットの一種であるため、
チーム価値観と会社価値観との一貫性を保つことができて、一体感があり、とても大きな力が発揮できそうです！

# 4、インタフェース分離の原則（ISP）
>Many client-specific interfaces are better than one general-purpose interface.
（汎用なインターフェースが一つあるよりも、各クライアントに特化したインターフェースがたくさんあった方がよい）
>
>※引用先: https://ja.wikipedia.org/wiki/SOLID

インタフェースの分離の原則から考える組織設計のヒントは「境界の分離」と「責務の分離」だと思います。

人間が認知の限界がありますので、一つのサービスの境界を分離をせず、いずれ人間の認知の限界を超えてしまいます。

それと、会社のよくあるあるですが、複数の異なる業務を１人で兼任することがよく聞きますね。
マルチタスクが非常にボトルネックになりやすく、そして個々のタスクのクオリティーも落ちやすいと思います。

それを解消するために、下記の図のように、ドメインと責務を分離し、メンバーの担当領域と種類を絞ると、個々のドメインと領域をより精錬、向上させることができる、且つ組織自体も拡張しやすくなると思います。(ですが、ずっと同じことがやるのはモチベーションが下がる懸念があるので、柔軟なチーム間のメンバー移動もできるとベストですね！)
![domain_a_b_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526011/ddf018cf-393e-c163-d8fa-45fd60ed9f48.jpeg)



# 5、依存関係逆転の原則（DIP）
>High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces), [not] concretions.
（上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも具象ではなく、抽象（インターフェースなど）に依存するべきである）
>
>※引用先: https://ja.wikipedia.org/wiki/SOLID

最後の依存関係逆転の原則（DIP）から組織設計を考えるヒントは **「属人化の排除」** だと思います。

「具象ではなく、抽象（インターフェースなど）に依存するべきである」と述べたように、組織の運営が人に依存すべきではなく、仕組みに依存すべきだと思います。

例えば、あるドメインチームがインフラ構成、サービスドメイン知識、テストパターン、デプロイのルールすべて可読性を意識し、コードに落とし込で、機能を開発をしています。もはやコードを読むだけで仕様をわかってしまう仕組みができているとします。

つまりエンジニアへの属人化(具象)への依存から、仕様(抽象)へ依存することになります。

この状態では、メンバーの異動や退職が発生しても、開発におけるベース知識があるエンジニアであれば、新規メンバーでも独自で仕様を理解することができるようになるので、組織を維持しやすくなりますね！

# まとめ

ちょっと長文となりましたが、SOLID原則から組織設計に適用し、**「メンテナンス性、生産性、拡張性の良い組織」** を考えてみました。

|  SOLID原則  |  組織に適用する概念  |
| ---- | ---- |
|  単一責任の原則（SRP）  |  与えられた責務範囲内の意思決定者を単一化する  |
|  オープン・クローズドの原則（OCP）  |  体制の基本構造を変更せずに拡張できる  |
|  リスコフの置換原則（LIP）  |  組織の一貫性を保つ  |
|  インタフェース分離の原則（ISP）  |  「境界の分離」と「責務の分離」  |
|  依存関係逆転の原則（DIP）  |  属人化の排除  |


実際に実践するとなると結構大変でたくさんの課題が出ると思いますが、
色んな人の意見を聞きながらより良い開発組織を作るように頑張っていきたいと思います！

以上、
ご覧いただき、ありがとうございました！