ソフトウェアは捨てやすく作ろう

この記事は[株式会社カオナビ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/kaonavi)の5日目の記事です。

https://qiita.com/advent-calendar/2024/kaonavi

## はじめに

ソフトウェア開発は、不確実性がとても高いです。

- 問題を解決するために提案されたプロダクトが、実際にその課題を適切に解決できるかどうかは分からない
- 実際に問題解決がされたところで、利益となるかは分からない
- プロダクトを開発するにあたり、期間などのリソースが正確に予測できない

こうした問題に対処するため、私たちは短期間での仮説検証を繰り返しながら、常にプロダクトを改善し続ける姿勢が必要になります。

この記事では、開発チームの立場から貢献するために「ソフトウェアは捨てやすく作りませんか？」という話をしたいと思います。

## どうして捨てやすく作るのがいいのか

仮説検証を繰り返しながらソフトウェアを開発していると、たびたび「必要だと思って作ったけど、やっぱり要らなかった」ものが出てきます。

次の図にあるようなスタートアップ・フィット・ジャーニーで言うところのPMF（Product Market Fit）前のプロダクトでは、実際のユーザーのニーズが掴みきれていないため、特に実験的に作った不要な機能が出てきやすくなります。

```plantuml
@startuml
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198450/964ef666-b787-b720-cce4-bcc68bdf85af.png>
caption ※引用：FoundX「スタートアップ・フィット・ジャーニー　今どの段階にいて、何に取り組むべきかのガイド」
@enduml
```
 
不要なコードを消した方がよさそうなことは、なんとなく理解いただけるかと思います。
しかし、次のような理由からコードを消すという作業は後回しにされがちです。

- ビジネス的には、別に消さなくてもユーザーから見た振る舞いは変わらない
- エンジニアリング的には、消したら何かが動作しなくなる可能性があってリスクが高い

そうした積み上げの結果、コードベースに対する認知負荷が上がるなどして、コード品質が下がり、ビジネスにも影響を与えてしまうことになります。

https://mtx2s.hatenablog.com/entry/2023/04/26/230917

そのため、できるだけコストをかけずに不要なコードを消せるよう、設計していく必要があると考えています。

## 捨てやすく作るために取り組んでいること

ソフトウェアを捨てやすく作るのがよいことは分かりましたが、実際何をしたらいいのでしょうか？

### チームで同じ方向を向く

まずは **「捨てやすく作る」** という方針を開発チーム全員で認識合わせしましょう。
チームで同じ方向を向くことで、後々の開発を進めやすくできます。

私たちのチームでは、インセプションデッキの中で、次のようなことを掲げました。

 - 捨てる勇気を持つ
 - 10分以内に機能が消せるように作る

 ```plantuml
@startuml
scale 400 width
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198450/3b8ecb50-099e-7152-2e64-1f67fe8451ab.png>
caption ※実際のインセプションデッキより一部引用
@enduml
```

このように言語化して共有することで、普段の開発中にも「捨てやすく作るため、〇〇な方向性にしましょう」といった会話がされるようになり、開発が進めやすくなったと思います。

### ディレクトリを機能単位で切る

実際に捨てやすく作るには、どうしたらいいのでしょうか？
私たちは、捨てやすく設計されたコードベースにするため、ディレクトリ構成をレイヤーに基づいた構成（Package by Layer）ではなく、フィーチャーに基づいた構成（Package by Feature）を採用しています。

具体的にイメージできるよう、Laravel で書かれているコードベースのディレクトリ構成での例を見てみましょう。（一部抜粋）

```:レイヤーに基づいたディレクトリ構成（Package by Layer）
/app
├─ Http
│  ├─ Feature1
│  ├─ Feature2
│  └─ Feature3
├─ Models
│  ├─ Feature1
│  ├─ Feature2
│  └─ Feature3
├─ Services
│  ├─ Feature1
│  ├─ Feature2
│  └─ Feature3
...
```

```:フィーチャーに基づいたディレクトリ構成（Package by Feature）
/app
├─ Feature1
│  ├─ Http
│  ├─ Models
│  └─ Services
├─ Feature2
│  ├─ Http
│  ├─ Models
│  └─ Services
├─ Feature3
│  ├─ Http
│  ├─ Models
│  └─ Services
...
```

このように Package by Feature は機能単位でファイルがまとまっています。これによって、**特定の機能についてのコードを探しやすく**なります。 
また、同時に機能を消す時には該当のディレクトリ配下をすべて削除するだけなので、**特定の機能をまとめて消しやすく**なります。

その際、依存関係に注意して、**高凝集** で **低結合** を実現できるようにしましょう。
せっかくディレクトリを分けても、結合度が高いとコードを消す際の依存関係が分かりにくくなってしまいます。

```plantuml
@startuml
scale 500 width
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198450/50c9e7c1-f309-34ab-bb5b-06372e40d9c5.png>
caption ※引用：Sahibinden Technology「Package by Layer vs Package by Feature」
@enduml
```

しかし、実際にやってみると開発規模が大きくなるにつれて、仮説検証したい細かい機能単位でディレクトリを切るのが辛くなってくるはずです。

そのため、私たちは次のようにディレクトリの階層構造を作ることで、仮説検証したい機能が多くなっても管理しやすくなるようにしています。

```:フィーチャーの中で更に細かい単位で区切る
/app
├─ Feature1
│  ├─ Feature1-1
│  │  ├─ Http
│  │  ├─ Models
│  │  └─ Services
│  ├─ Feature1-2
│  │  ├─ Http
│  │  ├─ Models
│  │  └─ Services
│  └─ Feature1-3
│     ├─ Http
│     ├─ Models
│     └─ Services
├─ Feature2
...
```

こうすることで、仮説検証したい細かい機能単位でディレクトリを切っても、管理しやすくなります。

## まとめ

- 短期間で仮説検証しながら開発するために、ソフトウェアは捨てやすく作る
- 捨てやすく作るために、機能単位でディレクトリを切る

このようにソフトウェア開発する上で捨てやすく作ることは、プロダクトの競争優位性を保つためにもオススメです。ぜひ取り入れてみてください。

## 参考資料

- [スタートアップ・フィット・ジャーニー　今どの段階にいて、何に取り組むべきかのガイド](https://review.foundx.jp/entry/startup-fit-journey) - FoundX
- [コード品質はやはりビジネスに影響を与える](https://mtx2s.hatenablog.com/entry/2023/04/26/230917) - mtx2s’s blog
- [Package by Layer vs Package by Feature](https://medium.com/sahibinden-technology/package-by-layer-vs-package-by-feature-7e89cde2ae3a) - Sahibinden Technology
- [ソフトウェア開発って なにか？を学ぶ勉強会](https://speakerdeck.com/kawaguti/software-development) - Yasunobu Kawaguchi
- [カオナビにおける マイクロサービスの取組と今後の展開](https://speakerdeck.com/hanhan1978/kaonavi-rearchitecturing) - Ryo Tomidokoro